public static WaypointGenerator Create(ConfigNode configNode, WaypointGeneratorFactory factory) { WaypointGenerator wpGenerator = new WaypointGenerator(); // Waypoint Manager integration EventData <string> onWaypointIconAdded = GameEvents.FindEvent <EventData <string> >("OnWaypointIconAdded"); bool valid = true; int index = 0; foreach (ConfigNode child in ConfigNodeUtil.GetChildNodes(configNode)) { DataNode dataNode = new DataNode("WAYPOINT_" + index, factory.dataNode, factory); try { ConfigNodeUtil.SetCurrentDataNode(dataNode); dataNode["type"] = child.name; double? altitude = null; WaypointData wpData = new WaypointData(child.name); // Use an expression to default - then it'll work for dynamic contracts if (!child.HasValue("targetBody")) { child.AddValue("targetBody", "@/targetBody"); } valid &= ConfigNodeUtil.ParseValue <CelestialBody>(child, "targetBody", x => wpData.waypoint.celestialName = x != null ? x.name : "", factory); valid &= ConfigNodeUtil.ParseValue <List <string> >(child, "name", x => wpData.names = x, factory, new List <string>()); valid &= ConfigNodeUtil.ParseValue <double?>(child, "altitude", x => altitude = x, factory, (double?)null); valid &= ConfigNodeUtil.ParseValue <List <string> >(child, "parameter", x => wpData.parameter = x, factory, new List <string>()); valid &= ConfigNodeUtil.ParseValue <bool>(child, "hidden", x => wpData.waypoint.visible = !x, factory, false); Action <string> assignWaypoint = (x) => { wpData.waypoint.id = x; if (onWaypointIconAdded != null) { onWaypointIconAdded.Fire(x); } }; if (!wpData.waypoint.visible) { valid &= ConfigNodeUtil.ParseValue <string>(child, "icon", assignWaypoint, factory, ""); } else { valid &= ConfigNodeUtil.ParseValue <string>(child, "icon", assignWaypoint, factory); } valid &= ConfigNodeUtil.ParseValue <bool>(child, "underwater", x => wpData.underwater = x, factory, false); valid &= ConfigNodeUtil.ParseValue <bool>(child, "clustered", x => wpData.waypoint.isClustered = x, factory, false); // Track the index wpData.waypoint.index = index++; // Get altitude if (altitude == null) { wpData.waypoint.altitude = 0.0; wpData.randomAltitude = true; } else { wpData.waypoint.altitude = altitude.Value; } // Get settings that differ by type if (child.name == "WAYPOINT") { valid &= ConfigNodeUtil.ParseValue <double>(child, "latitude", x => wpData.waypoint.latitude = x, factory); valid &= ConfigNodeUtil.ParseValue <double>(child, "longitude", x => wpData.waypoint.longitude = x, factory); } else if (child.name == "RANDOM_WAYPOINT") { // Get settings for randomization valid &= ConfigNodeUtil.ParseValue <bool>(child, "waterAllowed", x => wpData.waterAllowed = x, factory, true); valid &= ConfigNodeUtil.ParseValue <bool>(child, "forceEquatorial", x => wpData.forceEquatorial = x, factory, false); valid &= ConfigNodeUtil.ParseValue <int>(child, "count", x => wpData.count = x, factory, 1, x => Validation.GE(x, 1)); } else if (child.name == "RANDOM_WAYPOINT_NEAR") { // Get settings for randomization valid &= ConfigNodeUtil.ParseValue <bool>(child, "waterAllowed", x => wpData.waterAllowed = x, factory, true); // Get near waypoint details valid &= ConfigNodeUtil.ParseValue <int>(child, "nearIndex", x => wpData.nearIndex = x, factory, x => Validation.GE(x, 0) && Validation.LT(x, wpGenerator.waypoints.Count)); valid &= ConfigNodeUtil.ParseValue <bool>(child, "chained", x => wpData.chained = x, factory, false); valid &= ConfigNodeUtil.ParseValue <int>(child, "count", x => wpData.count = x, factory, 1, x => Validation.GE(x, 1)); // Get distances valid &= ConfigNodeUtil.ParseValue <double>(child, "minDistance", x => wpData.minDistance = x, factory, 0.0, x => Validation.GE(x, 0.0)); valid &= ConfigNodeUtil.ParseValue <double>(child, "maxDistance", x => wpData.maxDistance = x, factory, x => Validation.GT(x, 0.0)); } else if (child.name == "PQS_CITY") { wpData.randomAltitude = false; string dummy = null; valid &= ConfigNodeUtil.ParseValue <string>(child, "pqsCity", x => dummy = x, factory, x => { bool v = true; if (!string.IsNullOrEmpty(wpData.waypoint.celestialName)) { try { CelestialBody body = FlightGlobals.Bodies.Where(b => b.name == wpData.waypoint.celestialName).First(); wpData.pqsCity = body.GetComponentsInChildren <PQSCity>(true).Where(pqs => pqs.name == x).First(); } catch (Exception e) { LoggingUtil.LogError(typeof(WaypointGenerator), "Couldn't load PQSCity with name '{0}'", x); LoggingUtil.LogException(e); v = false; } } else { // Force this to get re-run when the targetBody is loaded throw new DataNode.ValueNotInitialized("/targetBody"); } return(v); }); valid &= ConfigNodeUtil.ParseValue <Vector3d>(child, "pqsOffset", x => wpData.pqsOffset = x, factory, new Vector3d()); } else if (child.name == "LAUNCH_SITE") { wpData.randomAltitude = false; string dummy = null; valid &= ConfigNodeUtil.ParseValue <string>(child, "launchSite", x => dummy = x, factory, x => { bool v = true; if (!string.IsNullOrEmpty(wpData.waypoint.celestialName)) { try { wpData.launchSite = ConfigNodeUtil.ParseLaunchSiteValue(x); } catch (Exception e) { LoggingUtil.LogError(typeof(WaypointGenerator), "Couldn't load Launch Site with name '{0}'", x); LoggingUtil.LogException(e); v = false; } } else { // Force this to get re-run when the targetBody is loaded throw new DataNode.ValueNotInitialized("/targetBody"); } return(v); }); valid &= ConfigNodeUtil.ParseValue <Vector3d>(child, "pqsOffset", x => wpData.pqsOffset = x, factory, new Vector3d()); } else { LoggingUtil.LogError(factory, "Unrecognized waypoint node: '{0}'", child.name); valid = false; } // Check for unexpected values valid &= ConfigNodeUtil.ValidateUnexpectedValues(child, factory); // Copy waypoint data WaypointData old = wpData; wpData = new WaypointData(old, null); wpGenerator.waypoints.Add(wpData); } finally { ConfigNodeUtil.SetCurrentDataNode(factory.dataNode); } } return(valid ? wpGenerator : null); }
public static WaypointGenerator Create(ConfigNode configNode, WaypointGeneratorFactory factory) { WaypointGenerator wpGenerator = new WaypointGenerator(); bool valid = true; int index = 0; foreach (ConfigNode child in ConfigNodeUtil.GetChildNodes(configNode)) { DataNode dataNode = new DataNode("WAYPOINT_" + index, factory.dataNode, factory); try { ConfigNodeUtil.SetCurrentDataNode(dataNode); dataNode["type"] = child.name; double? altitude = null; WaypointData wpData = new WaypointData(child.name); // Use an expression to default - then it'll work for dynamic contracts if (!child.HasValue("targetBody")) { child.AddValue("targetBody", "@/targetBody"); } valid &= ConfigNodeUtil.ParseValue <CelestialBody>(child, "targetBody", x => wpData.waypoint.celestialName = x != null ? x.name : "", factory); valid &= ConfigNodeUtil.ParseValue <List <string> >(child, "name", x => wpData.names = x, factory, new List <string>()); valid &= ConfigNodeUtil.ParseValue <double?>(child, "altitude", x => altitude = x, factory, (double?)null); valid &= ConfigNodeUtil.ParseValue <List <string> >(child, "parameter", x => wpData.parameter = x, factory, new List <string>()); valid &= ConfigNodeUtil.ParseValue <bool>(child, "hidden", x => wpData.waypoint.visible = !x, factory, false); if (!wpData.waypoint.visible) { valid &= ConfigNodeUtil.ParseValue <string>(child, "icon", x => wpData.waypoint.id = x, factory, ""); } else { valid &= ConfigNodeUtil.ParseValue <string>(child, "icon", x => wpData.waypoint.id = x, factory); } // The FinePrint logic is such that it will only look in Squad/Contracts/Icons for icons. // Cheat this by hacking the path in the game database. if (wpData.waypoint.id.Contains("/")) { GameDatabase.TextureInfo texInfo = GameDatabase.Instance.databaseTexture.Where(t => t.name == wpData.waypoint.id).FirstOrDefault(); if (texInfo != null) { texInfo.name = "Squad/Contracts/Icons/" + wpData.waypoint.id; } } valid &= ConfigNodeUtil.ParseValue <bool>(child, "underwater", x => wpData.underwater = x, factory, false); valid &= ConfigNodeUtil.ParseValue <bool>(child, "clustered", x => wpData.waypoint.isClustered = x, factory, false); // Track the index wpData.waypoint.index = index++; // Get altitude if (altitude == null) { wpData.waypoint.altitude = 0.0; wpData.randomAltitude = true; } else { wpData.waypoint.altitude = altitude.Value; } // Get settings that differ by type if (child.name == "WAYPOINT") { valid &= ConfigNodeUtil.ParseValue <double>(child, "latitude", x => wpData.waypoint.latitude = x, factory); valid &= ConfigNodeUtil.ParseValue <double>(child, "longitude", x => wpData.waypoint.longitude = x, factory); } else if (child.name == "RANDOM_WAYPOINT") { // Get settings for randomization valid &= ConfigNodeUtil.ParseValue <bool>(child, "waterAllowed", x => wpData.waterAllowed = x, factory, true); valid &= ConfigNodeUtil.ParseValue <bool>(child, "forceEquatorial", x => wpData.forceEquatorial = x, factory, false); valid &= ConfigNodeUtil.ParseValue <int>(child, "count", x => wpData.count = x, factory, 1, x => Validation.GE(x, 1)); } else if (child.name == "RANDOM_WAYPOINT_NEAR") { // Get settings for randomization valid &= ConfigNodeUtil.ParseValue <bool>(child, "waterAllowed", x => wpData.waterAllowed = x, factory, true); // Get near waypoint details valid &= ConfigNodeUtil.ParseValue <int>(child, "nearIndex", x => wpData.nearIndex = x, factory, x => Validation.GE(x, 0) && Validation.LT(x, wpGenerator.waypoints.Count)); valid &= ConfigNodeUtil.ParseValue <bool>(child, "chained", x => wpData.chained = x, factory, false); valid &= ConfigNodeUtil.ParseValue <int>(child, "count", x => wpData.count = x, factory, 1, x => Validation.GE(x, 1)); // Get distances valid &= ConfigNodeUtil.ParseValue <double>(child, "minDistance", x => wpData.minDistance = x, factory, 0.0, x => Validation.GE(x, 0.0)); valid &= ConfigNodeUtil.ParseValue <double>(child, "maxDistance", x => wpData.maxDistance = x, factory, x => Validation.GT(x, 0.0)); } else if (child.name == "PQS_CITY") { wpData.randomAltitude = false; string dummy = null; valid &= ConfigNodeUtil.ParseValue <string>(child, "pqsCity", x => dummy = x, factory, x => { bool v = true; if (!string.IsNullOrEmpty(wpData.waypoint.celestialName)) { try { CelestialBody body = FlightGlobals.Bodies.Where(b => b.name == wpData.waypoint.celestialName).First(); wpData.pqsCity = body.GetComponentsInChildren <PQSCity>(true).Where(pqs => pqs.name == x).First(); } catch (Exception e) { LoggingUtil.LogError(typeof(WaypointGenerator), "Couldn't load PQSCity with name '" + x + "'"); LoggingUtil.LogException(e); v = false; } } else { // Force this to get re-run when the targetBody is loaded throw new DataNode.ValueNotInitialized("/targetBody"); } return(v); }); valid &= ConfigNodeUtil.ParseValue <Vector3d>(child, "pqsOffset", x => wpData.pqsOffset = x, factory, new Vector3d()); } else { LoggingUtil.LogError(factory, "Unrecognized waypoint node: '" + child.name + "'"); valid = false; } // Check for unexpected values valid &= ConfigNodeUtil.ValidateUnexpectedValues(child, factory); // Copy waypoint data WaypointData old = wpData; for (int i = 0; i < old.count; i++) { wpData = new WaypointData(old, null); wpGenerator.waypoints.Add(wpData); if (old.parameter.Any()) { wpData.parameter = new List <string>(); wpData.parameter.Add(old.parameter.Count() == 1 ? old.parameter.First() : old.parameter.ElementAtOrDefault(i)); } // Set the name if (old.names.Any()) { wpData.waypoint.name = (old.names.Count() == 1 ? old.names.First() : old.names.ElementAtOrDefault(i)); } if (string.IsNullOrEmpty(wpData.waypoint.name) || wpData.waypoint.name.ToLower() == "site") { wpData.waypoint.name = StringUtilities.GenerateSiteName(random.Next(), wpData.waypoint.celestialBody, !wpData.waterAllowed); } // Handle waypoint chaining if (wpData.chained && i != 0) { wpData.nearIndex = wpGenerator.waypoints.Count - 2; } } } finally { ConfigNodeUtil.SetCurrentDataNode(factory.dataNode); } } return(valid ? wpGenerator : null); }
public static SpawnKerbal Create(ConfigNode configNode, SpawnKerbalFactory factory) { SpawnKerbal spawnKerbal = new SpawnKerbal(); bool valid = true; int index = 0; foreach (ConfigNode child in ConfigNodeUtil.GetChildNodes(configNode, "KERBAL")) { DataNode dataNode = new DataNode("KERBAL_" + index++, factory.dataNode, factory); try { ConfigNodeUtil.SetCurrentDataNode(dataNode); KerbalData kd = new KerbalData(); // Use an expression to default - then it'll work for dynamic contracts if (!child.HasValue("targetBody")) { child.AddValue("targetBody", "@/targetBody"); } valid &= ConfigNodeUtil.ParseValue <CelestialBody>(child, "targetBody", x => kd.body = x, factory); // Get landed stuff if (child.HasValue("lat") && child.HasValue("lon") || child.HasValue("pqsCity")) { kd.landed = true; if (child.HasValue("pqsCity")) { string pqsCityStr = null; valid &= ConfigNodeUtil.ParseValue <string>(child, "pqsCity", x => pqsCityStr = x, factory); if (pqsCityStr != null) { try { kd.pqsCity = kd.body.GetComponentsInChildren <PQSCity>(true).Where(pqs => pqs.name == pqsCityStr).First(); } catch (Exception e) { LoggingUtil.LogError(typeof(WaypointGenerator), "Couldn't load PQSCity with name '" + pqsCityStr + "'"); LoggingUtil.LogException(e); valid = false; } } valid &= ConfigNodeUtil.ParseValue <Vector3d>(child, "pqsOffset", x => kd.pqsOffset = x, factory, new Vector3d()); // Don't expect these to load anything, but do it to mark as initialized valid &= ConfigNodeUtil.ParseValue <double>(child, "lat", x => kd.latitude = x, factory, 0.0); valid &= ConfigNodeUtil.ParseValue <double>(child, "lon", x => kd.longitude = x, factory, 0.0); } else { valid &= ConfigNodeUtil.ParseValue <double>(child, "lat", x => kd.latitude = x, factory); valid &= ConfigNodeUtil.ParseValue <double>(child, "lon", x => kd.longitude = x, factory); } valid &= ConfigNodeUtil.ParseValue <float>(child, "heading", x => kd.heading = x, factory, 0.0f); } // Get orbit else if (child.HasNode("ORBIT")) { // Don't expect these to load anything, but do it to mark as initialized valid &= ConfigNodeUtil.ParseValue <double>(child, "lat", x => kd.latitude = x, factory, 0.0); valid &= ConfigNodeUtil.ParseValue <double>(child, "lon", x => kd.longitude = x, factory, 0.0); valid &= ConfigNodeUtil.ParseValue <Orbit>(child, "ORBIT", x => kd.orbit = x, factory); } else { // Will error valid &= ConfigNodeUtil.ValidateMandatoryChild(child, "ORBIT", factory); } valid &= ConfigNodeUtil.ParseValue <double?>(child, "alt", x => kd.altitude = x, factory, (double?)null); if (child.HasValue("kerbal")) { valid &= ConfigNodeUtil.ParseValue <Kerbal>(child, "kerbal", x => kd.kerbal = x, factory); } else { // Default gender if (!child.HasValue("gender")) { child.AddValue("gender", "Random()"); } valid &= ConfigNodeUtil.ParseValue <ProtoCrewMember.Gender>(child, "gender", x => kd.kerbal.gender = x, factory); // Default name if (!child.HasValue("name")) { child.AddValue("name", "RandomKerbalName(@gender)"); } valid &= ConfigNodeUtil.ParseValue <string>(child, "name", x => { kd.kerbal.name = x; if (kd.kerbal.pcm != null) { kd.kerbal.pcm.ChangeName(x); } }, factory); } // Get additional stuff valid &= ConfigNodeUtil.ParseValue <bool>(child, "owned", x => kd.owned = x, factory, false); valid &= ConfigNodeUtil.ParseValue <bool>(child, "addToRoster", x => kd.addToRoster = x, factory, true); valid &= ConfigNodeUtil.ParseValue <ProtoCrewMember.KerbalType>(child, "kerbalType", x => kd.kerbal.kerbalType = x, factory, ProtoCrewMember.KerbalType.Unowned); // Check for unexpected values valid &= ConfigNodeUtil.ValidateUnexpectedValues(child, factory); // Add to the list spawnKerbal.kerbals.Add(kd); } finally { ConfigNodeUtil.SetCurrentDataNode(factory.dataNode); } } return(valid ? spawnKerbal : null); }
public static OrbitGenerator Create(ConfigNode configNode, OrbitGeneratorFactory factory) { OrbitGenerator obGenerator = new OrbitGenerator(); bool valid = true; int index = 0; foreach (ConfigNode child in ConfigNodeUtil.GetChildNodes(configNode)) { DataNode dataNode = new DataNode("ORBIT_" + index++, factory.dataNode, factory); try { ConfigNodeUtil.SetCurrentDataNode(dataNode); OrbitData obData = new OrbitData(child.name); // Get settings that differ by type if (child.name == "FIXED_ORBIT") { valid &= ConfigNodeUtil.ParseValue <Orbit>(child, "ORBIT", x => obData.orbit = x, factory); } else if (child.name == "RANDOM_ORBIT") { valid &= ConfigNodeUtil.ParseValue <OrbitType>(child, "type", x => obData.orbitType = x, factory); valid &= ConfigNodeUtil.ParseValue <int>(child, "count", x => obData.count = x, factory, 1, x => Validation.GE(x, 1)); valid &= ConfigNodeUtil.ParseValue <double>(child, "altitudeFactor", x => obData.altitudeFactor = x, factory, 0.8, x => Validation.Between(x, 0.0, 1.0)); valid &= ConfigNodeUtil.ParseValue <double>(child, "inclinationFactor", x => obData.inclinationFactor = x, factory, 0.8, x => Validation.Between(x, 0.0, 1.0)); valid &= ConfigNodeUtil.ParseValue <double>(child, "eccentricity", x => obData.eccentricity = x, factory, 0.0, x => Validation.GE(x, 0.0)); valid &= ConfigNodeUtil.ParseValue <double>(child, "deviationWindow", x => obData.deviationWindow = x, factory, 10.0, x => Validation.GE(x, 0.0)); } else { throw new ArgumentException("Unrecognized orbit node: '" + child.name + "'"); } // Use an expression to default - then it'll work for dynamic contracts if (!child.HasValue("targetBody")) { child.AddValue("targetBody", "@/targetBody"); } valid &= ConfigNodeUtil.ParseValue <CelestialBody>(child, "targetBody", x => obData.targetBody = x, factory); // Check for unexpected values valid &= ConfigNodeUtil.ValidateUnexpectedValues(child, factory); // Add to the list obGenerator.orbits.Add(obData); if (dataNode.IsInitialized("targetBody") && dataNode.IsInitialized("type")) { valid &= obGenerator.ValidateOrbitType(obData, factory); } } finally { ConfigNodeUtil.SetCurrentDataNode(factory.dataNode); } } return(valid ? obGenerator : null); }
public static SpawnVessel Create(ConfigNode configNode, SpawnVesselFactory factory) { SpawnVessel spawnVessel = new SpawnVessel(); ConfigNodeUtil.ParseValue <bool>(configNode, "deferVesselCreation", x => spawnVessel.deferVesselCreation = x, factory, false); bool valid = true; int index = 0; foreach (ConfigNode child in ConfigNodeUtil.GetChildNodes(configNode, "VESSEL")) { DataNode dataNode = new DataNode("VESSEL_" + index++, factory.dataNode, factory); try { ConfigNodeUtil.SetCurrentDataNode(dataNode); VesselData vessel = new VesselData(); // Get name if (child.HasValue("name")) { valid &= ConfigNodeUtil.ParseValue <string>(child, "name", x => vessel.name = x, factory); } // Get craft details if (child.HasValue("craftURL")) { valid &= ConfigNodeUtil.ParseValue <string>(child, "craftURL", x => vessel.craftURL = x, factory); } if (child.HasValue("craftPart")) { valid &= ConfigNodeUtil.ParseValue <AvailablePart>(child, "craftPart", x => vessel.craftPart = x, factory); } valid &= ConfigNodeUtil.AtLeastOne(child, new string[] { "craftURL", "craftPart" }, factory); valid &= ConfigNodeUtil.ParseValue <string>(child, "flagURL", x => vessel.flagURL = x, factory, (string)null); valid &= ConfigNodeUtil.ParseValue <VesselType>(child, "vesselType", x => vessel.vesselType = x, factory, VesselType.Ship); // Use an expression to default - then it'll work for dynamic contracts if (!child.HasValue("targetBody")) { child.AddValue("targetBody", "@/targetBody"); } valid &= ConfigNodeUtil.ParseValue <CelestialBody>(child, "targetBody", x => vessel.body = x, factory); // Get landed stuff if (child.HasValue("pqsCity")) { string pqsCityStr = null; valid &= ConfigNodeUtil.ParseValue <string>(child, "pqsCity", x => pqsCityStr = x, factory); if (pqsCityStr != null) { try { vessel.pqsCity = vessel.body.GetComponentsInChildren <PQSCity>(true).Where(pqs => pqs.name == pqsCityStr).First(); } catch (Exception e) { LoggingUtil.LogError(typeof(WaypointGenerator), "Couldn't load PQSCity with name '" + pqsCityStr + "'"); LoggingUtil.LogException(e); valid = false; } } valid &= ConfigNodeUtil.ParseValue <Vector3d>(child, "pqsOffset", x => vessel.pqsOffset = x, factory, new Vector3d()); // Don't expect these to load anything, but do it to mark as initialized valid &= ConfigNodeUtil.ParseValue <double>(child, "lat", x => vessel.latitude = x, factory, 0.0); valid &= ConfigNodeUtil.ParseValue <double>(child, "lon", x => vessel.longitude = x, factory, 0.0); // Do load alt and height valid &= ConfigNodeUtil.ParseValue <double?>(child, "alt", x => vessel.altitude = x, factory, (double?)null); valid &= ConfigNodeUtil.ParseValue <float>(child, "height", x => vessel.height = x, factory, !string.IsNullOrEmpty(vessel.craftURL) ? 0.0f : 2.5f); vessel.orbiting = false; } else if (child.HasValue("lat") && child.HasValue("lon")) { valid &= ConfigNodeUtil.ParseValue <double>(child, "lat", x => vessel.latitude = x, factory); valid &= ConfigNodeUtil.ParseValue <double>(child, "lon", x => vessel.longitude = x, factory); valid &= ConfigNodeUtil.ParseValue <double?>(child, "alt", x => vessel.altitude = x, factory, (double?)null); valid &= ConfigNodeUtil.ParseValue <float>(child, "height", x => vessel.height = x, factory, !string.IsNullOrEmpty(vessel.craftURL) ? 0.0f : 2.5f); vessel.orbiting = false; } // Get orbit else { valid &= ConfigNodeUtil.ParseValue <Orbit>(child, "ORBIT", x => vessel.orbit = x, factory); vessel.orbiting = true; } valid &= ConfigNodeUtil.ParseValue <float>(child, "heading", x => vessel.heading = x, factory, 0.0f); valid &= ConfigNodeUtil.ParseValue <float>(child, "pitch", x => vessel.pitch = x, factory, 0.0f); valid &= ConfigNodeUtil.ParseValue <float>(child, "roll", x => vessel.roll = x, factory, 0.0f); // Get additional flags valid &= ConfigNodeUtil.ParseValue <bool>(child, "owned", x => vessel.owned = x, factory, false); // Handle the CREW nodes foreach (ConfigNode crewNode in ConfigNodeUtil.GetChildNodes(child, "CREW")) { int count = 1; valid &= ConfigNodeUtil.ParseValue <int>(crewNode, "count", x => count = x, factory, 1); for (int i = 0; i < count; i++) { CrewData cd = new CrewData(); // Read crew details valid &= ConfigNodeUtil.ParseValue <string>(crewNode, "name", x => cd.name = x, factory, (string)null); valid &= ConfigNodeUtil.ParseValue <bool>(crewNode, "addToRoster", x => cd.addToRoster = x, factory, true); // Check for unexpected values valid &= ConfigNodeUtil.ValidateUnexpectedValues(crewNode, factory); // Add the record vessel.crew.Add(cd); } } // Check for unexpected values valid &= ConfigNodeUtil.ValidateUnexpectedValues(child, factory); // Add to the list spawnVessel.vessels.Add(vessel); } finally { ConfigNodeUtil.SetCurrentDataNode(factory.dataNode); } } if (!configNode.HasNode("VESSEL")) { valid = false; LoggingUtil.LogError(factory, "SpawnVessel requires at least one VESSEL node."); } return(valid ? spawnVessel : null); }