コード例 #1
0
        //public static Thing FindBestMedicine(Pawn healer, Pawn patient)
        public static List <ThingCount> Find(Pawn healer, Pawn patient, out int totalCount)
        {
            totalCount = 0;
            Log.Message($"{healer} is tending to {patient}");

            float sufficientQuality = maxMedicineQuality + 1;             // nothing is sufficient!

            if (Settings.Get().minimalMedicineForNonUrgent)
            {
                if (patient.health.hediffSet.hediffs.All(h => !h.TendableNow() || !h.IsUrgent()))
                {
                    sufficientQuality = minMedicineQuality;
                    Log.Message($"Sufficient medicine for non-urgent care is {sufficientQuality}");
                }
            }

            MedicalCareCategory defaultCare = patient.GetCare();

            //Care setting
            MedicalCareCategory finalCare = MedicalCareCategory.NoCare;
            var           hediffCare      = PriorityCareComp.Get();
            List <Hediff> hediffsToTend   = HediffsToTend(patient);

            Log.Message($"Tending ({hediffsToTend.ToStringSafeEnumerable()})");
            foreach (Hediff h in hediffsToTend)
            {
                MedicalCareCategory toUse = defaultCare;
                if (hediffCare.TryGetValue(h, out MedicalCareCategory heCare))
                {
                    toUse = heCare;
                }

                finalCare = toUse > finalCare ? toUse : finalCare;
            }
            Log.Message($"defaultCare = {defaultCare}, care = {finalCare}");

            //Android Droid support;
            Predicate <Thing> validatorDroid = t => true;
            Type extMechanicalPawn           = AccessTools.TypeByName("Androids.MechanicalPawnProperties");
            bool isDroid = extMechanicalPawn != null && (patient.def.modExtensions?.Any(e => extMechanicalPawn.IsAssignableFrom(e.GetType())) ?? false);

            if (isDroid)
            {
                Log.Message($"{patient} is a droid");
                Type extRepair = AccessTools.TypeByName("Androids.DroidRepairProperties");
                validatorDroid = t => t.def.modExtensions?.Any(e => extRepair.IsAssignableFrom(e.GetType())) ?? false;
            }

            //Med
            Predicate <Thing> validatorMed = t => finalCare.AllowsMedicine(t.def) && validatorDroid(t);

            //Ground
            Map               map            = patient.Map;
            TraverseParms     traverseParams = TraverseParms.For(healer, Danger.Deadly, TraverseMode.ByPawn, false);
            Predicate <Thing> validator      = (Thing t) => validatorMed(t) &&
                                               map.reachability.CanReach(patient.Position, t, PathEndMode.ClosestTouch, traverseParams) &&
                                               !t.IsForbidden(healer) && healer.CanReserve(t, FindBestMedicine.maxPawns, 1);//can reserve at least 1
            Func <Thing, float> priorityGetter  = (Thing t) => MedicineRating(t, sufficientQuality);
            List <Thing>        groundMedicines = patient.Map.listerThings.ThingsInGroup(isDroid?ThingRequestGroup.HaulableEver:ThingRequestGroup.Medicine).FindAll(t => validator(t));

            //Pawns
            Predicate <Pawn> validatorHolder = (Pawn p) =>
                                               map.reachability.CanReach(patient.Position, p, PathEndMode.ClosestTouch, traverseParams);

            List <Pawn> pawns = healer.Map.mapPawns.SpawnedPawnsInFaction(Faction.OfPlayer).ListFullCopy();

            if (!Settings.Get().useDoctorMedicine)
            {
                pawns.Remove(healer);
            }
            if (!Settings.Get().usePatientMedicine)
            {
                pawns.Remove(patient);
            }
            if (!Settings.Get().useColonistMedicine)
            {
                pawns.RemoveAll(p => p.IsFreeColonist && p != healer && p != patient);
            }
            if (!Settings.Get().useAnimalMedicine)
            {
                pawns.RemoveAll(p => !p.IsColonist);
            }

            int minDistance = DistanceTo(healer, patient);

            if (!Settings.Get().useOtherEvenIfFar)
            {
                pawns.RemoveAll(p => DistanceTo(p, healer, patient) > minDistance + Settings.Get().distanceToUseFromOther * 2);                 //*2, there and back
            }
            pawns.RemoveAll(p => !validatorHolder(p));

            //Evaluate them all
            List <MedicineEvaluator> allMeds = new List <MedicineEvaluator>();

            //Add each ground
            foreach (Thing t in groundMedicines)
            {
                allMeds.Add(new MedicineEvaluator()
                {
                    thing    = t,
                    pawn     = null,
                    rating   = MedicineRating(t, sufficientQuality),
                    distance = DistanceTo(t, healer, patient)
                });
            }

            List <MedicineEvaluator> groundEvaluators = allMeds.ListFullCopy();

            //Add best from each pawn
            foreach (Pawn p in pawns)
            {
                Thing t = FindBestMedicineInInventory(p, patient, validatorMed, sufficientQuality, p == healer);
                if (t == null)
                {
                    continue;
                }
                allMeds.Add(new MedicineEvaluator()
                {
                    thing    = t,
                    pawn     = p,
                    rating   = MedicineRating(t, sufficientQuality),
                    distance = DistanceTo(p, healer, patient)
                });
            }

            //Find best
            allMeds.Sort();


            foreach (MedicineEvaluator tryMed in allMeds)
            {
                tryMed.DebugLog();
            }

            MedicineEvaluator bestMed = allMeds.LastOrDefault();

            List <ThingCount> result = new List <ThingCount>();

            if (bestMed.thing != null)
            {
                allMeds.RemoveLast();
                if (Settings.Get().useCloseMedicine&& bestMed.pawn != null)
                {
                    bestMed.DebugLog("Best: ");
                    Log.Message($"checking nearby:");
                    List <MedicineEvaluator> equalMedicines = groundEvaluators.FindAll(eval => eval.rating == bestMed.rating);
                    if (equalMedicines.Count > 0)
                    {
                        MedicineEvaluator closeMed = equalMedicines.MinBy(eval => eval.distance);
                        closeMed.DebugLog("Nearby med on the way there: ");
                        if (closeMed.distance <= minDistance + Settings.Get().distanceToUseEqualOnGround * 2)                         //*2, there and back
                        {
                            bestMed = closeMed;
                        }
                    }
                }

                //Check if any minimal medicine exists
                if (allMeds.Count == 0 || bestMed.rating == allMeds.MinBy(m => m.rating).rating)
                {
                    GetMedicineCountToFullyHeal_Patch.__beep_beep_MinimalMedicineAvailable = false;
                    Log.Message($"No minimal medicine available");
                }
                int count = Medicine.GetMedicineCountToFullyHeal(patient);

                totalCount = count;
                Log.Message($"Medicine count = {count}");

                bestMed.DebugLog("Best Med on " + (bestMed.pawn == null ? "ground" : "hand") + ":");

                int usedCount = Mathf.Min(bestMed.thing.stackCount, count);
                result.Add(new ThingCount(bestMed.thing, usedCount));
                count -= usedCount;
                //Find some more needed nearby
                if (count > 0)
                {
                    List <MedicineEvaluator> equalMedicines = allMeds.FindAll(eval => eval.rating == bestMed.rating);
                    equalMedicines.SortBy(eval => DistanceTo(bestMed.pawn ?? bestMed.thing, eval.pawn ?? eval.thing));
                    Thing droppedMedicine = null;
                    Log.Message($"But needs {count} more");
                    while (count > 0 && equalMedicines.Count > 0)
                    {
                        MedicineEvaluator closeMed = equalMedicines.First();
                        equalMedicines.RemoveAt(0);

                        closeMed.DebugLog("More: ");

                        if (DistanceTo(droppedMedicine ?? bestMed.pawn ?? bestMed.thing, closeMed.pawn ?? closeMed.thing) > 8f)                         //8f as defined in CheckForGetOpportunityDuplicate
                        {
                            break;
                        }

                        usedCount = Mathf.Min(closeMed.thing.stackCount, count);
                        closeMed.DebugLog("Using: ({usedCount})");

                        result.Add(new ThingCount(closeMed.thing, usedCount));
                        count -= usedCount;
                    }
                }
            }
            return(result);
        }
コード例 #2
0
        private static bool Prefix(Pawn healer, Pawn patient, ref Thing __result)
        {
            if (patient.playerSettings == null || patient.playerSettings.medCare <= MedicalCareCategory.NoMeds ||
                !healer.Faction.IsPlayer)
            {
                return(true);
            }

            Log.Message(healer + " is tending to " + patient);

            float sufficientQuality = maxMedicineQuality + 1;             // nothing is sufficient!

            if (Settings.Get().downgradeExcessiveMedicine)
            {
                sufficientQuality = CalculateSufficientQuality(healer, patient);
                Log.Message("Sufficient medicine for best treatment is " + sufficientQuality + "(" + Settings.Get().goodEnoughDowngradeFactor + ")");
            }
            if (Settings.Get().minimalMedicineForNonUrgent)
            {
                if (patient.health.hediffSet.hediffs.All(h => !h.TendableNow || !h.IsUrgent()))
                {
                    sufficientQuality = minMedicineQuality;
                    Log.Message("Sufficient medicine for non-urgent care is " + sufficientQuality);
                }
            }

            //Med
            Predicate <Thing> validatorMed = t => patient.playerSettings.medCare.AllowsMedicine(t.def);

            try
            {
                ((Action)(() =>
                {
                    MedicalCareCategory pharmacistAdvice = Pharmacist.PharmacistUtility.TendAdvice(patient);
                    validatorMed = t => pharmacistAdvice.AllowsMedicine(t.def);
                }))();
            }
            catch (Exception) { }

            //Ground
            Map               map            = patient.Map;
            TraverseParms     traverseParams = TraverseParms.For(healer, Danger.Deadly, TraverseMode.ByPawn, false);
            Predicate <Thing> validator      = (Thing t) => validatorMed(t) &&
                                               map.reachability.CanReach(patient.Position, t, PathEndMode.ClosestTouch, traverseParams) &&
                                               !t.IsForbidden(healer) && healer.CanReserve(t, FindBestMedicine.maxPawns, 1);//can reserve at least 1
            Func <Thing, float> priorityGetter  = (Thing t) => MedicineRating(t, sufficientQuality);
            List <Thing>        groundMedicines = patient.Map.listerThings.ThingsInGroup(ThingRequestGroup.Medicine).FindAll(t => validator(t));

            //Pawns
            Predicate <Pawn> validatorHolder = (Pawn p) =>
                                               map.reachability.CanReach(patient.Position, p, PathEndMode.ClosestTouch, traverseParams);

            List <Pawn> pawns = healer.Map.mapPawns.SpawnedPawnsInFaction(Faction.OfPlayer).ListFullCopy();

            if (!Settings.Get().useDoctorMedicine)
            {
                pawns.Remove(healer);
            }
            if (!Settings.Get().usePatientMedicine)
            {
                pawns.Remove(patient);
            }
            if (!Settings.Get().useColonistMedicine)
            {
                pawns.RemoveAll(p => p.IsFreeColonist && p != healer && p != patient);
            }
            if (!Settings.Get().useAnimalMedicine)
            {
                pawns.RemoveAll(p => !p.IsColonist);
            }

            int minDistance = DistanceTo(healer, patient);

            if (!Settings.Get().useOtherEvenIfFar)
            {
                pawns.RemoveAll(p => DistanceTo(p, healer, patient) > minDistance + Settings.Get().distanceToUseFromOther * 2);                 //*2, there and back
            }
            pawns.RemoveAll(p => !validatorHolder(p));

            //Evaluate them all
            List <MedicineEvaluator> allMeds = new List <MedicineEvaluator>();

            //Add each ground
            foreach (Thing t in groundMedicines)
            {
                allMeds.Add(new MedicineEvaluator()
                {
                    thing    = t,
                    pawn     = null,
                    rating   = MedicineRating(t, sufficientQuality),
                    distance = DistanceTo(t, healer, patient)
                });
            }

            List <MedicineEvaluator> groundEvaluators = allMeds.ListFullCopy();

            //Add best from each pawn
            foreach (Pawn p in pawns)
            {
                Thing t = FindBestMedicineInInventory(p, patient, validatorMed, sufficientQuality, p == healer);
                if (t == null)
                {
                    continue;
                }
                allMeds.Add(new MedicineEvaluator()
                {
                    thing    = t,
                    pawn     = p,
                    rating   = MedicineRating(t, sufficientQuality),
                    distance = DistanceTo(p, healer, patient)
                });
            }

            //Find best
            allMeds.Sort();


            foreach (MedicineEvaluator tryMed in allMeds)
            {
                tryMed.DebugLog();
            }

            MedicineEvaluator bestMed = allMeds.LastOrDefault();

            if (bestMed.thing != null)
            {
                allMeds.RemoveLast();
                if (Settings.Get().useCloseMedicine&& bestMed.pawn != null)
                {
                    bestMed.DebugLog("Best: ");
                    Log.Message("checking nearby:");
                    List <MedicineEvaluator> equalMedicines = groundEvaluators.FindAll(eval => eval.rating == bestMed.rating);
                    if (equalMedicines.Count > 0)
                    {
                        MedicineEvaluator closeMed = equalMedicines.MinBy(eval => eval.distance);
                        closeMed.DebugLog("Nearby med on the way there: ");
                        if (closeMed.distance <= minDistance + Settings.Get().distanceToUseEqualOnGround * 2)                         //*2, there and back
                        {
                            bestMed = closeMed;
                        }
                    }
                }

                //Check if any minimal medicine exists
                if (allMeds.Count == 0 || bestMed.rating == allMeds.MinBy(m => m.rating).rating)
                {
                    GetMedicineCountToFullyHeal_Patch.__beep_beep_MinimalMedicineAvailable = false;
                    Log.Message("No minimal medicine available");
                }
                int count = Medicine.GetMedicineCountToFullyHeal(patient);

                JobOnThing_Patch.medCount = count;
                Log.Message("FindBestMedicine count = " + count);

                if (bestMed.pawn == null)
                {
                    bestMed.DebugLog("Best Med on ground:");
                    __result = bestMed.thing;
                }
                else
                {
                    // because The Toil to get this medicine is FailOnDespawnedNullOrForbidden
                    // And Medicine in inventory is despawned
                    // You can't set the job to use already carried medicine.
                    // Editing the toil would be more difficult.
                    // But we can drop it so the normal job picks it back it  ¯\_(ツ)_/¯
                    bestMed.DebugLog("Best Med on hand: ");

                    //Drop it!
                    int dropCount = Mathf.Min(bestMed.thing.stackCount, count);
                    count   -= dropCount;
                    __result = bestMed.thing;
                    //	return true;

                    //Todo: mid-job getting new medicine fails since this isn't dropped mid-toil. Sokay since it just fails and restarts.
                    //if healer job is tend maybe?

                    //Find some more needed nearby
                    //bestMed is dropped in Notify_Start, but these aren't tracked there so they are dropped now.

                    //In a very odd case that you right-click assign a tend and a second pawn's medicine is needed, he might drop his entire inventory
                    // That's a vanilla thing though that calls JobOnThing over and over instead of HasJobOnThing
                    if (count > 0)
                    {
                        List <MedicineEvaluator> equalMedicines = allMeds.FindAll(eval => eval.rating == bestMed.rating);
                        equalMedicines.SortBy(eval => DistanceTo(bestMed.pawn, eval.pawn ?? eval.thing));
                        Thing droppedMedicine = null;
                        Log.Message("But needs " + count + " more");
                        while (count > 0 && equalMedicines.Count > 0)
                        {
                            MedicineEvaluator closeMed = equalMedicines.First();
                            equalMedicines.RemoveAt(0);

                            closeMed.DebugLog("More: ");

                            if (DistanceTo(droppedMedicine ?? bestMed.pawn, closeMed.pawn ?? closeMed.thing) > 8f)                             //8f as defined in CheckForGetOpportunityDuplicate
                            {
                                return(false);
                            }

                            dropCount = Mathf.Min(closeMed.thing.stackCount, count);
                            closeMed.DebugLog("Using: (" + dropCount + ")");

                            closeMed.pawn?.inventory.innerContainer.TryDrop(closeMed.thing, ThingPlaceMode.Near, dropCount, out droppedMedicine);
                            if (droppedMedicine != null && !droppedMedicine.IsForbidden(healer) && healer.CanReserve(droppedMedicine, maxPawns, count))
                            {
                                count -= dropCount;
                            }
                            //else return false;
                        }
                    }
                }

                //Use it!
                //Log.Message("using inventory " + medicine);
                //healer.carryTracker.innerContainer.TryAddOrTransfer(medicine, count);
                //__result = medicine;
            }
            return(__result == null);
        }