public static void Listener(Pawn healer, Pawn patient, ref Thing __result) { try { //On ne soccupe que des patient étant des androids /*if (!) * return true;*/ if (Settings.androidsCanUseOrganicMedicine) { return; } bool patientIsAndroid = Utils.ExceptionAndroidList.Contains(patient.def.defName) || patient.IsCyberAnimal(); if (patient.playerSettings == null || patient.playerSettings.medCare <= MedicalCareCategory.NoMeds) { __result = null; return; } if (Medicine.GetMedicineCountToFullyHeal(patient) <= 0) { __result = null; return; } Predicate <Thing> predicate; //COmpatibilité avec pharmacist, le medoc renvoyé doit avoir une quantitée de soin inferieur ou egal à celui renvoyé par les appels précédents float medicalPotency = 0; if (__result != null) { medicalPotency = __result.def.GetStatValueAbstract(StatDefOf.MedicalPotency, null); } if (patientIsAndroid) { predicate = (Thing m) => Utils.ExceptionNanoKits.Contains(m.def.defName) && m.def.GetStatValueAbstract(StatDefOf.MedicalPotency, null) <= medicalPotency && !m.IsForbidden(healer) && patient.playerSettings.medCare.AllowsMedicine(m.def) && healer.CanReserve(m, 10, 1, null, false); } else { predicate = (Thing m) => !Utils.ExceptionNanoKits.Contains(m.def.defName) && m.def.GetStatValueAbstract(StatDefOf.MedicalPotency, null) <= medicalPotency && !m.IsForbidden(healer) && !m.IsForbidden(healer) && patient.playerSettings.medCare.AllowsMedicine(m.def) && healer.CanReserve(m, 10, 1, null, false); } Func <Thing, float> priorityGetter = (Thing t) => t.def.GetStatValueAbstract(StatDefOf.MedicalPotency, null); IntVec3 position = patient.Position; Map map = patient.Map; List <Thing> searchSet = patient.Map.listerThings.ThingsInGroup(ThingRequestGroup.Medicine); PathEndMode peMode = PathEndMode.ClosestTouch; TraverseParms traverseParams = TraverseParms.For(healer, Danger.Deadly, TraverseMode.ByPawn, false); Predicate <Thing> validator = predicate; __result = GenClosest.ClosestThing_Global_Reachable(position, map, searchSet, peMode, traverseParams, 9999f, validator, priorityGetter); } catch (Exception e) { Log.Message("[ATPP] HealthAIUtility.FindBestMedicine(Error) : " + e.Message + " - " + e.StackTrace); } }
protected override Job TryGiveJob(Pawn pawn) { Pawn patient = GetTendableColonist(pawn.Position, pawn.Map); if (patient != null) { Thing medicine = null; if (Medicine.GetMedicineCountToFullyHeal(patient) > 0) { medicine = FindBestUnforbiddenMedicine(pawn, patient); } if (medicine != null) { return(new Job(JobDefOf.TendPatient, patient, medicine)); } return(new Job(JobDefOf.TendPatient, patient)); } else { Lord lord = pawn.GetLord(); if (lord != null) { lord.ReceiveMemo("HealFinished"); } } return(null); }
// Token: 0x0600002D RID: 45 RVA: 0x00002758 File Offset: 0x00000958 public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParm) { bool flag = !HealthAIUtility.ShouldSeekMedicalRest(pawn); ThinkResult result; if (flag) { result = ThinkResult.NoJob; } else { bool flag2 = !HealthAIUtility.ShouldBeTendedNowByPlayer(pawn); if (flag2) { result = ThinkResult.NoJob; } else { bool flag3 = !GenCollection.Any <Apparel>(pawn.apparel.WornApparel, (Apparel x) => x.def.defName.Contains("RRY_Equipment_HunterGauntlet")); if (flag3) { result = ThinkResult.NoJob; } else { Thing thing = RestUtility.FindPatientBedFor(pawn); bool flag4 = thing == null; if (flag4) { result = ThinkResult.NoJob; } else { Thing thing2 = null; bool flag5 = Medicine.GetMedicineCountToFullyHeal(pawn) > 0; if (flag5) { thing2 = HealthAIUtility.FindBestMedicine(pawn, pawn); } bool flag6 = thing2 != null; Job job; if (flag6) { job = new Job(YautjaDefOf.RRY_Yautja_TendSelf, thing, thing2); } else { job = new Job(YautjaDefOf.RRY_Yautja_TendSelf, thing); } result = new ThinkResult(job, this, null, false); } } } } return(result); }
//FindBestMedicine Replacement private static bool Prefix(Pawn healer, Pawn patient, ref Thing __result) { if (patient.GetCare() <= MedicalCareCategory.NoMeds || Medicine.GetMedicineCountToFullyHeal(patient) <= 0) { return(true); } __result = Find(healer, patient, out int dummy).FirstOrDefault().Thing; return(false); }
public void SendMedicIfNeeded() { if (Find.TickManager.TicksGame >= this.nextTendableColonistCheckTick) { this.nextTendableColonistCheckTick = Find.TickManager.TicksGame + tendableColonistCheckPeriodInTicks; // Check if spaceship is about to take off. if (IsTakeOffImminent(2 * Util_Spaceship.medicsRecallBeforeTakeOffMarginInTicks)) { return; } // Get a healthy medic. Pawn medic = null; foreach (Pawn pawn in this.medics.InRandomOrder()) { if (this.pawnsAboard.Contains(pawn) && (pawn.health.HasHediffsNeedingTend() == false)) { medic = pawn; break; } } if (medic == null) { return; } // Look for tendable colonist. Pawn patient = JobGiver_HealColonists.GetTendableColonist(this.Position, this.Map); if (patient == null) { return; } // Spawn needed medikits if available. if (this.availableMedikitsCount > 0) { int neededMedikitsCount = Medicine.GetMedicineCountToFullyHeal(patient); int medikitsToSpawnCount = Math.Min(this.availableMedikitsCount, neededMedikitsCount); if (medikitsToSpawnCount > 0) { SpawnItem(ThingDefOf.MedicineIndustrial, null, medikitsToSpawnCount, this.Position, this.Map, 0f); this.availableMedikitsCount -= medikitsToSpawnCount; } } // Spawn medic. GenSpawn.Spawn(medic, this.Position, this.Map); this.pawnsAboard.Remove(medic); Lord lord = LordMaker.MakeNewLord(Util_Faction.MiningCoFaction, new LordJob_HealColonists(this.Position), this.Map); lord.AddPawn(medic); } }
//Stupid re-write because I want count. //public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false) public static bool Prefix(Pawn pawn, Thing t, ref Job __result) { medCount = 0; Pawn patient = t as Pawn; Thing thing = null; if (Medicine.GetMedicineCountToFullyHeal(patient) > 0) { thing = HealthAIUtility.FindBestMedicine(pawn, patient); } Log.Message("JobOnThing count = " + medCount); __result = new Job(JobDefOf.TendPatient, patient, thing) { count = medCount }; return(false); }
public override void DesignateSingleCell(IntVec3 c) { List <Thing> thingList = c.GetThingList(); foreach (var thing in thingList) { Pawn pawn = thing as Pawn; if (pawn != null && pawn.health.ShouldBeTendedNow) { Job jobNew = new Job(DefDatabase <JobDef> .GetNamed("ApplyMedicine")); jobNew.targetA = pawn; jobNew.targetB = medicine; jobNew.maxNumToCarry = Medicine.GetMedicineCountToFullyHeal(jobNew.targetA.Thing as Pawn); doctor.drafter.TakeOrderedJob(jobNew); break; } } DesignatorManager.Deselect(); }
//Stupid re-write because I want count. //public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false) public static bool Prefix(Pawn pawn, Thing t, ref Job __result) { Pawn patient = t as Pawn; if (Medicine.GetMedicineCountToFullyHeal(patient) > 0) { List <ThingCount> meds = FindBestMedicine.Find(pawn, patient, out int medCount); Job job = new Job(JobDefOf.TendPatient, patient, meds.FirstOrDefault().Thing); job.count = medCount; if (meds.Count() > 1) { job.targetQueueA = meds.Skip(1).Select(med => new LocalTargetInfo(med.Thing)).ToList(); job.countQueue = meds.Skip(1).Select(med => med.Count).ToList(); } __result = job; } else { __result = new Job(JobDefOf.TendPatient, patient); } return(false); }
public override void DesignateSingleCell(IntVec3 c) { List <Thing> thingList = c.GetThingList(); foreach (Thing thing in thingList) { Pawn pawn = thing as Pawn; if (pawn != null && pawn.health.ShouldBeTendedNow) { Job jobNew = new Job(HaulJobDefOf.ApplyMedicine); jobNew.targetA = pawn; Thing dummy; SlotsBackpackComp.slots.TryDrop(medicine, doctor.Position, ThingPlaceMode.Direct, Medicine.GetMedicineCountToFullyHeal(jobNew.targetA.Thing as Pawn), out dummy); jobNew.targetB = dummy; jobNew.maxNumToCarry = Medicine.GetMedicineCountToFullyHeal(jobNew.targetA.Thing as Pawn); doctor.drafter.TakeOrderedJob(jobNew); break; } } DesignatorManager.Deselect(); }
//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); }
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); }
public static void Listener(Pawn healer, Pawn patient, ref Thing __result) { try { /*if (!) * return true;*/ if (Settings.androidsCanUseOrganicMedicine) { return; } var patientIsAndroid = Utils.ExceptionAndroidList.Contains(patient.def.defName) || patient.IsCyberAnimal(); if (patient.playerSettings == null || patient.playerSettings.medCare <= MedicalCareCategory.NoMeds) { __result = null; return; } if (Medicine.GetMedicineCountToFullyHeal(patient) <= 0) { __result = null; return; } Predicate <Thing> predicate; float medicalPotency = 0; if (__result != null) { medicalPotency = __result.def.GetStatValueAbstract(StatDefOf.MedicalPotency); } if (patientIsAndroid) { predicate = m => Utils.ExceptionNanoKits.Contains(m.def.defName) && m.def.GetStatValueAbstract(StatDefOf.MedicalPotency) <= medicalPotency && !m.IsForbidden(healer) && patient.playerSettings.medCare.AllowsMedicine(m.def) && healer.CanReserve(m, 10, 1); } else { predicate = m => !Utils.ExceptionNanoKits.Contains(m.def.defName) && m.def.GetStatValueAbstract(StatDefOf.MedicalPotency) <= medicalPotency && !m.IsForbidden(healer) && !m.IsForbidden(healer) && patient.playerSettings.medCare.AllowsMedicine(m.def) && healer.CanReserve(m, 10, 1); } Func <Thing, float> priorityGetter = t => t.def.GetStatValueAbstract(StatDefOf.MedicalPotency); var position = patient.Position; var map = patient.Map; var searchSet = patient.Map.listerThings.ThingsInGroup(ThingRequestGroup.Medicine); var peMode = PathEndMode.ClosestTouch; var traverseParams = TraverseParms.For(healer); var validator = predicate; __result = GenClosest.ClosestThing_Global_Reachable(position, map, searchSet, peMode, traverseParams, 9999f, validator, priorityGetter); } catch (Exception e) { Log.Message("[ATPP] HealthAIUtility.FindBestMedicine(Error) : " + e.Message + " - " + e.StackTrace); } }