/// <summary>
 /// Here when the player manually changes the crew assignments in the editor.
 /// </summary>
 /// <param name="construct"></param>
 private void OnCrewChanged(ShipConstruct construct)
 {
     if (Crewable.CanList(construct))
     {
         Logging.Log("Crew edited, persisting kerbal assignments.");
         AssignmentLogic.PersistKerbalAssignments(construct);
         LogVesselManifest();
     }
 }
 public CrewableList(ShipConstruct construct)
 {
     allCrewables     = Crewable.List(construct);
     commandCrewables = new List <Crewable>();
     foreach (Crewable crewable in allCrewables)
     {
         if (crewable.IsCommand)
         {
             commandCrewables.Add(crewable);
         }
     }
 }
 /// <summary>
 /// Here when the ship is modified.
 /// </summary>
 /// <param name="construct"></param>
 private void OnShipModified(ShipConstruct construct)
 {
     try
     {
         if (Crewable.CanList(construct))
         {
             Logging.Log("Ship modified, " + construct.Count + " parts.");
             AssignmentLogic.AssignKerbals(construct);
         }
     }
     catch (Exception e)
     {
         Logging.Exception(e);
     }
 }
        /// <summary>
        /// If the ship has no probe cores with SAS, try to make sure that there's at least one pilot.
        /// Returns the available SAS level (-1 if no SAS present).
        /// </summary>
        /// <param name="construct"></param>
        /// <param name="crewables"></param>
        private static int TryEnsureSAS(ShipConstruct construct, CrewableList crewables)
        {
            // Do we have SAS?
            int sasLevel = GetHighestSASLevel(construct);

            // get the highest-level pilot
            ProtoCrewMember highestPilot = GetHighestAssignedLevel(crewables.Command, PILOT_PROFESSION);
            int             pilotLevel   = (highestPilot == null) ? -1 : highestPilot.experienceLevel;

            int maxSas = (sasLevel > pilotLevel) ? sasLevel : pilotLevel;

            if (maxSas >= 0)
            {
                // we already have SAS on the ship, don't need to add a pilot
                if ((sasLevel < 0) && (pilotLevel > sasLevel))
                {
                    Logging.Log(Logging.ToString(highestPilot) + " is already assigned and will provide SAS");
                }
                return(maxSas);
            }

            // There's no SAS control, we need to add a pilot somewhere.

            // Try to find a slot to put a pilot
            CrewSlot pilotSlot = Crewable.FindEmptySlot(crewables.Command);

            if (pilotSlot == null)
            {
                Logging.Warn("SAS will be unavailable (no probe cores, no open slots to add a pilot)");
                return(maxSas);
            }

            // Try to find a pilot to assign.
            ProtoCrewMember lowestPilot;

            if (!FindHighestLowestAvailable(PILOT_PROFESSION, 0, crewables, out highestPilot, out lowestPilot))
            {
                Logging.Warn("SAS will be unavailable (no probe cores, no available pilots)");
                return(maxSas);
            }

            Logging.Log("Assigning " + Logging.ToString(highestPilot) + " to provide SAS");
            pilotSlot.Occupant = highestPilot;
            return(highestPilot.experienceLevel);
        }
 /// <summary>
 /// Here when an event happens to the part in the editor.
 /// </summary>
 /// <param name="eventType"></param>
 /// <param name="part"></param>
 private void OnEditorPartEvent(ConstructionEventType eventType, Part part)
 {
     try
     {
         if (eventType != ConstructionEventType.PartAttached)
         {
             return;
         }
         if (Crewable.CanList(CurrentShipConstruct))
         {
             Logging.Log("Attached " + Logging.ToString(part) + ".");
             AssignmentLogic.AssignKerbals(CurrentShipConstruct);
         }
     }
     catch (Exception e)
     {
         Logging.Exception(e);
     }
 }
        /// <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
        }
 /// <summary>
 /// Determines whether the specified crew member is assigned to anything in the list.
 /// </summary>
 /// <param name="crewMember"></param>
 /// <returns></returns>
 public bool IsAssigned(ProtoCrewMember crewMember)
 {
     return(Crewable.IsAssigned(allCrewables, crewMember));
 }