public PayloadResource Clone() { PayloadResource copy = new PayloadResource(); copy.name = name; copy.amount = amount; copy.mass = mass; return(copy); }
// Returns a list of resources which the given vessel has available capacity to receive in an transport-mission: public static List <PayloadResource> GetFreeResourcesCapacities(Vessel vessel) { var availableResources = new List <PayloadResource>(); try { foreach (var protoPart in vessel.protoVessel.protoPartSnapshots) { foreach (var protoResource in protoPart.resources) { // We manipulate resources of unloaded vessels in "AddResources" below, which does not update "protoResource.amount", so we have // to read the config-node here: var free = protoResource.maxAmount - protoResource.amount; if (free < 0.01) { continue; // Too small amounts would get shown as 0.00, which would be confusing, so we ignore them just like 0. } if (!KSTS.resourceDictionary.ContainsKey(protoResource.resourceName)) { continue; } var resource = KSTS.resourceDictionary[protoResource.resourceName]; if (resource.density <= 0) { continue; } var availableResource = availableResources.Find(x => x.name == protoResource.resourceName); if (availableResource != null) { availableResource.amount += free; } else { availableResource = new PayloadResource(); availableResource.amount = free; availableResource.mass = resource.density; availableResource.name = protoResource.resourceName; availableResources.Add(availableResource); } } } } catch (Exception e) { Debug.LogError("[KSTS] GetFreeResourcesCapacities(" + vessel.vesselName.ToString() + "): " + e.ToString()); } return(availableResources); }
// Gathers all important current stats for the given vessel (eg its mass, price, etc): public static RecordingVesselStats GetStats(FlightRecording recording, Vessel vessel) { RecordingVesselStats stats = new RecordingVesselStats(); stats.hasCrew = vessel.GetCrewCount() > 0; stats.payloadResources = new Dictionary <string, PayloadResource>(); stats.dockingPortTypes = new List <string>(); foreach (Part part in vessel.parts) { string partName = SanitizeParteName(part.name); // Check for modules which enable the vessel to perform certain actions: List <ModuleDockingNode> dockingNodes = part.FindModulesImplementing <ModuleDockingNode>(); if (dockingNodes.Count() > 0) { stats.hasDockingPort = true; foreach (ModuleDockingNode dockingNode in dockingNodes) { if (!stats.dockingPortTypes.Contains(dockingNode.nodeType.ToString())) { stats.dockingPortTypes.Add(dockingNode.nodeType.ToString()); // Docking-nodes have differnt sizes, like "node0", "node1", etc } } } if (part.FindModulesImplementing <ModuleDecouple>().Count() > 0) { stats.hasSeperator = true; } if (part.FindModulesImplementing <ModuleAnchoredDecoupler>().Count() > 0) { stats.hasSeperator = true; } if (part.FindModulesImplementing <ModuleRCS>().Count() > 0) { stats.hasRCS = true; } // Mass of the part and its resources: stats.mass += part.mass + part.resourceMass; // Sum up all the parts resources: double resourceCost = 0; double resourceCostMax = 0; foreach (PartResource resource in part.Resources) { PartResourceDefinition resourceDefinition = null; if (!KSTS.resourceDictionary.TryGetValue(resource.resourceName.ToString(), out resourceDefinition)) { Debug.LogError("[KSTS] RecordingVesselStats.GetStats(): resource '" + resource.resourceName.ToString() + "' not found in dictionary"); } else { // Cost: resourceCost += (double)(resource.amount * resourceDefinition.unitCost); resourceCostMax += (double)(resource.maxAmount * resourceDefinition.unitCost); // Track remaining amout for payload delivery (only resources which have a weight): if (resourceDefinition.density > 0 && resource.amount > 0) { PayloadResource payloadResource = null; if (stats.payloadResources.TryGetValue(resource.resourceName.ToString(), out payloadResource)) { stats.payloadResources.Remove(resource.resourceName.ToString()); payloadResource.amount += resource.amount; } else { payloadResource = new PayloadResource(); payloadResource.amount = resource.amount; payloadResource.name = resource.resourceName.ToString(); payloadResource.mass = resourceDefinition.density; } stats.payloadResources.Add(resource.resourceName.ToString(), payloadResource); } } } stats.cost += resourceCost; // The cost of the part is only available in the AvailablePart-class: AvailablePart availablePart = null; if (!KSTS.partDictionary.TryGetValue(partName, out availablePart)) { Debug.LogError("[KSTS] RecordingVesselStats.GetStats(): part '" + partName + "' not found in dictionary"); } else { // The cost of the part already includes the resource-costs, when completely filled: double dryCost = availablePart.cost - resourceCostMax; stats.cost += dryCost; } } // Find all crewed parts: List <string> crewedPartIds = new List <string>(); foreach (ProtoCrewMember crewMember in vessel.GetVesselCrew()) { if (crewMember.seat == null || crewMember.seat.part == null) { continue; } if (crewedPartIds.Contains(crewMember.seat.part.flightID.ToString())) { continue; } crewedPartIds.Add(crewMember.seat.part.flightID.ToString()); } // Find all valid subassemblies, which can be detached from the control-part: stats.payloadAssemblies = new Dictionary <string, PayloadAssembly>(); stats.FindPayloadAssemblies(recording, vessel.rootPart, null, crewedPartIds); return(stats); }
public void DeployPayloadResources(Dictionary <string, double> requestedResources) { double dumpedMass = 0; double dumpedFunds = 0; if (!this.CanPerformMission(MissionProfileType.TRANSPORT)) { return; } // Try to dump the requested amount of resources from the vessel: foreach (var item in requestedResources) { string requestedName = item.Key; double requestedAmount = item.Value; PayloadResource payloadResource = null; if (!this.currentStats.payloadResources.TryGetValue(requestedName, out payloadResource)) { continue; } if (requestedAmount > payloadResource.amount) { requestedAmount = payloadResource.amount; } if (requestedAmount < 0) { requestedAmount = 0; } // Find parts to dump the resources from: double amountToDump = requestedAmount; foreach (Part part in vessel.parts) { if (amountToDump <= 0) { break; } double dumpedAmount = part.RequestResource(requestedName, amountToDump); if (dumpedAmount == 0) { continue; } Debug.Log("[KSTS] dumped " + dumpedAmount.ToString() + " of " + requestedName.ToString() + " from " + part.name.ToString()); amountToDump -= dumpedAmount; dumpedMass += dumpedAmount * payloadResource.mass; if (KSTS.resourceDictionary.ContainsKey(payloadResource.name)) { dumpedFunds += dumpedAmount * KSTS.resourceDictionary[payloadResource.name].unitCost; } } } // If there was something dumped, we can move on to the next stage: if (dumpedMass > 0) { this.payloadMass = dumpedMass; this.missionType = MissionProfileType.TRANSPORT; this.deploymentTime = Planetarium.GetUniversalTime(); this.status = FlightRecordingStatus.DESCENDING; this.dockingPortTypes = this.currentStats.dockingPortTypes; // The weight and value of the payload should not counted in the vessel's stats: this.launchMass -= dumpedMass; this.launchCost -= Math.Round(dumpedFunds); } }
// Shows a list of all available payload-resources the player can choose from: public bool DisplayList() { GUILayout.BeginHorizontal(); GUILayout.Label("<size=14><b>Cargo:</b></size>"); string[] transportTypeStrings = new string[] { "Resources", "Crew" }; selectedTransportType = GUILayout.Toolbar(selectedTransportType, transportTypeStrings); GUILayout.EndHorizontal(); scrollPos = GUILayout.BeginScrollView(scrollPos, GUI.scrollStyle); if (selectedTransportType == 0) { // Transport Resources: selectedCrewTransfers = null; if (resourceSelectors.Count == 0) { GUILayout.Label("The selected target has no free capacity to receive resources."); } else { // Show list with all possible payloads: int index = 0; double selectedMass = 0; List <PayloadResource> currentlySelectedPayloads = new List <PayloadResource>(); foreach (GUIRichValueSelector selector in resourceSelectors) { selector.Display(); PayloadResource resource = availableResources[index]; PayloadResource selected = resource.Clone(); selected.amount = selector.Value; currentlySelectedPayloads.Add(selected); selectedMass += selected.amount * selected.mass; index++; } // Show total selected amount: string textColor = "#00FF00"; if (selectedMass > missionProfile.payloadMass) { textColor = "#FF0000"; } GUILayout.BeginHorizontal(); GUILayout.Label("<b>Selected Payload:</b>"); GUILayout.Label("<color=" + textColor + ">" + selectedMass.ToString("#,##0.00 t") + " / " + missionProfile.payloadMass.ToString("#,##0.00 t") + "</color> ", new GUIStyle(GUI.labelStyle) { alignment = TextAnchor.MiddleRight }); GUILayout.EndHorizontal(); // If the selected mass falls in the range of the transport capacity, we can use the current selection for the mission: if (selectedMass > 0 && selectedMass <= missionProfile.payloadMass) { selectedResources = currentlySelectedPayloads; } else { selectedResources = null; } } } else { // Transport Crew: selectedResources = null; bool validCrewSelection = crewTransferSelector.DisplayList(); // If there is a valid selection, copy the selection: if (validCrewSelection && (crewTransferSelector.crewToDeliver.Count > 0 || crewTransferSelector.crewToCollect.Count > 0)) { selectedCrewTransfers = new List <CrewTransferOrder>(); foreach (string name in crewTransferSelector.crewToDeliver) { selectedCrewTransfers.Add(new CrewTransferOrder() { kerbalName = name, direction = CrewTransferOrder.CrewTransferDirection.DELIVER }); } foreach (string name in crewTransferSelector.crewToCollect) { selectedCrewTransfers.Add(new CrewTransferOrder() { kerbalName = name, direction = CrewTransferOrder.CrewTransferDirection.COLLECT }); } } else { selectedCrewTransfers = null; } } GUILayout.EndScrollView(); return(selectedResources != null || selectedCrewTransfers != null); }