/// <summary> /// Record all preferred crew assignments for the ship. /// </summary> /// <param name="construct"></param> private void LogPreferredAssignments(ShipConstruct construct) { foreach (Part part in construct.Parts) { Assignment[] assignments = Assignment.GetSlotAssignments(part); for (int index = 0; index < assignments.Length; ++index) { String message = Logging.ToString(part) + " slot " + index + ": "; Assignment assignment = assignments[index]; if (assignment == null) { message += "unassigned"; } else { message += "assign " + assignment; } Logging.Log(message); } // for each crew slot on the part foreach (ModuleCrewRequirement requirement in ModuleCrewRequirement.CrewRequirementsOf(part)) { Logging.Log(Logging.ToString(part) + ": require " + requirement); } } // for each part on the ship }
/// <summary> /// Find all professions required by parts on the ship. Key is profession name, value is /// the requirement that introduced it. /// </summary> /// <param name="construct"></param> /// <returns></returns> private static Dictionary <string, ModuleCrewRequirement> FindRequiredProfessions(ShipConstruct construct) { Dictionary <string, ModuleCrewRequirement> requirements = new Dictionary <string, ModuleCrewRequirement>(); foreach (Part part in construct.parts) { foreach (ModuleCrewRequirement requirement in part.Modules.GetModules <ModuleCrewRequirement>()) { string profession = requirement.profession; if ((profession == null) || (profession.Length == 0)) { continue; } if (requirements.ContainsKey(profession)) { ModuleCrewRequirement previousRequirement = requirements[profession]; if (requirement.importance > previousRequirement.importance) { requirements[profession] = requirement; // it's more important, replace it } else if (requirement.importance == previousRequirement.importance) { if (requirement.minimumLevel > previousRequirement.minimumLevel) { // They're equally important, but one of them needs a higher level. Go with the lower one. requirements[profession] = requirement; } } } else { // first requirement for this profession, add it requirements[profession] = requirement; } } } return(requirements); }
/// <summary> /// As needed, assign additional crew members to fulfill part requirements. /// </summary> /// <param name="crewables"></param> /// <param name="construct"></param> private static void FulfillPartRequirements(CrewableList crewables, ShipConstruct construct) { // Find any professions that are required on the ship Dictionary <string, ModuleCrewRequirement> requirements = FindRequiredProfessions(construct); if (requirements.Count == 0) { return; // there aren't any requirements } // We can ignore any that are already provided for List <string> ignoreList = new List <string>(); foreach (string requiredProfession in requirements.Keys) { int requiredLevel = requirements[requiredProfession].minimumLevel; if (HasProfession(crewables.All, requiredProfession, requiredLevel)) { ignoreList.Add(requiredProfession); } } foreach (string ignoreProfession in ignoreList) { requirements.Remove(ignoreProfession); } if (requirements.Count == 0) { return; // all requirements already taken care of } // We now have a set of required professions that we want to have on board, // but haven't yet satisfied. There might not be enough slots to hold them all, // so build a prioritized list with the most-desired profession first. List <String> prioritizedRequirements = new List <string>(requirements.Keys); if (prioritizedRequirements.Count > 1) { // Sort our remaining requirements in descending order of importance Comparison <string> byPriority = (profession1, profession2) => { // First, sort by *declared* importance from the parts. i.e. if the // Mystery Goo asks for a scientist and assigns importance 0, and the // ISRU asks for an engineer and assigns importance 1, then the ISRU wins. int importance1 = requirements[profession1].importance; int importance2 = requirements[profession2].importance; if (importance1 > importance2) { return(-1); } if (importance2 > importance1) { return(1); } // In case of a tie (e.g. one part asks for a scientist with importance 1 // and the other asks for an engineer with importance 1), then pick the // profession that's "better". importance1 = ProfessionImportance(profession1); importance2 = ProfessionImportance(profession2); if (importance1 > importance2) { return(-1); } if (importance2 > importance1) { return(1); } return(0); }; prioritizedRequirements.Sort(byPriority); } // Now go through our prioritized profession requirements and try to find // an empty slot (and available unassigned crew member) to satisfy each one. foreach (string requiredProfession in prioritizedRequirements) { ModuleCrewRequirement requirement = requirements[requiredProfession]; string part = Logging.ToString(requirement.part); ProtoCrewMember highest; ProtoCrewMember lowest; if (FindHighestLowestAvailable(requiredProfession, requirement.minimumLevel, crewables, out highest, out lowest)) { // Got a crew member to fulfill the requirement. In the case of pilots, // we want the lowest-level possible (to leave the highest freed up to // satisfy SAS requirements for other ships). But for anyone else, we // want the highest available. ProtoCrewMember crew = PILOT_PROFESSION.Equals(requiredProfession) ? lowest : highest; // Is there a command slot available? CrewSlot slot = Crewable.FindEmptySlot(crewables.Command); if (slot == null) { // okay then, how about a non-command slot? slot = Crewable.FindEmptySlot(crewables.All); } if (slot == null) { Logging.Warn("No open slot is available to assign a " + requirement.Description + " to operate " + part); } else { slot.Occupant = crew; Logging.Log("Assigning " + Logging.ToString(crew) + " to operate " + part); } } else { // there's nobody to fill the slot Logging.Warn("No " + requirement.Description + " is available to operate " + part + ", not assigning anyone"); } } // for each required profession }