internal static bool IsValidFoodSourceForPawn(this Thing food, Pawn eater, Pawn getter, Policy policy, bool allowForbidden) { try { bool canManipulate = getter.RaceProps.ToolUser && getter.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation); bool inventory = (getter.inventory != null && getter.inventory.innerContainer.Contains(food)); if (!(food != null && (inventory || food.Spawned) && !food.Destroyed && (allowForbidden || !food.IsForbidden(getter)) && (!food.def.IsIngestible || food.IngestibleNow) && (inventory || food.IsSociallyProper(eater) || food.IsSociallyProper(getter)) && //TODO: eater == getter ? getter.CanReachFoodSource(food) && (inventory || getter.CanReserve(food)) )) { return(false); } if (food is Pawn) { if (((Pawn)food).RaceProps.Humanlike || food.Map.designationManager.AllDesignationsOn(food).Any(arg => arg.def == DesignationDefOf.Tame) || !FoodUtils.IsAcceptablePreyFor(eater, food as Pawn) || !policy.PolicyAllows(FoodCategory.Hunt) || Utils.IsAnyoneCapturing(food.Map, food as Pawn) // redundant with humanlike ? ) { return(false); } } else { var category = food.DetermineFoodCategory(); if (!eater.RaceProps.CanEverEat((food is Building_NutrientPasteDispenser) ? ((Building_NutrientPasteDispenser)food).DispensableDef : food.def) || ((food is Building_NutrientPasteDispenser) && (!((Building_NutrientPasteDispenser)food).CanDispenseNow || !canManipulate)) || !policy.PolicyAllows(category) || category == FoodCategory.Ignore || category == FoodCategory.Luxury) { return(false); } //TODO: plant pot avoidance not tested if (food.def.plant != null && food.Position.GetThingList(food.Map).Any((obj) => obj.def == ThingDef.Named("PlantPot"))) { return(false); } } return(true); } catch (Exception ex) { throw new Exception(string.Format("food={0} eater={1} policy={2}", food, eater, policy), ex); } }
internal static bool TryGetEntryForPawn(Pawn getter, Pawn eater, out PawnEntry foodSourceEntry, bool allowForbidden) { var pawnPair = new PawnPair(eater, getter); if (AllByPawnPair.TryGetValue(pawnPair, out foodSourceEntry)) { if (!foodSourceEntry.Expired() && (foodSourceEntry.BestFood == null || // Accepts null FoodUtils.IsValidFoodSourceForPawn(foodSourceEntry.BestFood, eater, getter, eater.GetPolicyAssignedTo(), allowForbidden))) { #if DEBUG //Log.Message(string.Format("Found food entry for {0}/{1} = {2}", eater, getter, foodSourceEntry.BestFood)); #endif return(true); } else { #if DEBUG Log.Message(string.Format("Deleted expired food entry for {0} = {1}", pawnPair, foodSourceEntry.BestFood)); #endif AllByPawnPair.Remove(pawnPair); } } else { #if DEBUG Log.Message(string.Format("No food entry found for {0}", pawnPair)); #endif } foodSourceEntry = null; return(false); }
//TODO: optimize function internal static FoodSourceRating FoodScoreFor(Policy policy, Pawn eater, Pawn getter, Thing food, bool noDistanceFactor = false, bool quickScore = false) { var obj = new FoodSourceRating(); obj.FoodSource = food; obj.DefRecord = food.GetFoodDefRecord(); // ------ Distance factor ------ bool inInventory = (getter.inventory != null && getter.inventory.innerContainer.Contains(food)); if (!noDistanceFactor) { float distanceFactor; if (inInventory) { distanceFactor = 0f; } else { distanceFactor = -((food.Position - eater.Position).LengthManhattan) * policy.distanceFactor; } obj.AddComp("Distance", distanceFactor); } if (inInventory && getter == eater && food.def.ingestible.preferability >= FoodPreferability.MealAwful && (eater.IsColonistPlayerControlled && (food.GetFoodCategory() != FoodCategory.MealSurvival))) //added to prevent survival meals from being eaten out of inventory { obj.AddComp("Inventory", 500f); } // ------------- Policy food category factor ------------- //if (!policy.unrestricted) // obj.AddComp(food.DetermineFoodCategory().ToString(), policy.GetFoodScoreOffset(eater, food)); // ------------- Prey score factor ------------- const float PREY_FACTOR_MULTIPLIER = 5f; //Because the vanilla prey factor is fairly weak. { if (food is Pawn) { var preyScore = RimWorld.FoodUtility.GetPreyScoreFor(eater, food as Pawn) * PREY_FACTOR_MULTIPLIER; //ducktape, negates the distance factor of the vanilla function as it is already calculated by the mod. preyScore += (eater.Position - food.Position).LengthHorizontal * PREY_FACTOR_MULTIPLIER; obj.AddComp("Prey (ratio=" + FoodUtils.GetPreyRatio(eater, food as Pawn).ToString("F2") + ")", preyScore); return(obj); } } // ------------- Food def optimality offset ------------- if (policy.optimalityOffsetMatters && food.def.ingestible != null) { float optimalityOffset; if (eater.NonHumanlikeOrWildMan()) { optimalityOffset = food.def.ingestible.optimalityOffsetFeedingAnimals; } else { optimalityOffset = food.def.ingestible.optimalityOffsetHumanlikes; } obj.AddComp("Def", food.def.ingestible.optimalityOffsetHumanlikes); } if (!quickScore) { // ------------- Mood effect factor ------------- //float num2 = 0f; if (eater.needs != null && eater.needs.mood != null && policy.moodEffectMatters) { List <ThoughtDef> list; if (!(food is RimWorld.Building_NutrientPasteDispenser)) { list = RimWorld.FoodUtility.ThoughtsFromIngesting(eater, food, food.def); } else { list = ((RimWorld.Building_NutrientPasteDispenser)food).GetBestMealThoughtsFor(eater); } for (int i = 0; i < list.Count; i++) { obj.AddComp(list[i].defName, policy.moodEffectFactor * Detours.Access.FoodOptimalityEffectFromMoodCurve.Evaluate(list[i].stages[0].baseMoodEffect)); } } // ------------- Cost factor (waste factor aswell) ------------- //TODO: concurrent based waste factor. float costRatio; float foodNutrition; var curFoodLevel = eater.needs.food.CurLevel; var maxFoodLevel = eater.needs.food.MaxLevel; if (food.def.IsFoodDispenser) { foodNutrition = ThingDefOf.MealNutrientPaste.ingestible.CachedNutrition; } else if (food.def.IsCorpse) { var corpse = food as Corpse; foodNutrition = RimWorld.FoodUtility.GetBodyPartNutrition(corpse, corpse.GetBestBodyPartToEat(eater, curFoodLevel)); } else { foodNutrition = food.def.ingestible.CachedNutrition; } if (foodNutrition >= 0.1f) { //TODO: ajust; add record for the NPD if (food is RimWorld.Building_NutrientPasteDispenser) { costRatio = (food.def.building.nutritionCostPerDispense * 0.05f) / ThingDefOf.MealNutrientPaste.ingestible.CachedNutrition; } else { costRatio = (obj.DefRecord.costFactor); } if (food.def.IsCorpse) { costRatio *= 0.25f; } //TODO: combine waste & cost factors float actualMealCost = costRatio * foodNutrition; float costFactorOffset = Math.Max(0, actualMealCost - (maxFoodLevel - curFoodLevel)) * -Config.CostFactor /* * policy.costFactorMultiplier*/; //* Config.CostFactor * policy.costFactorMultiplier; if (costFactorOffset != 0f) { obj.AddComp("Hunger (min=" + actualMealCost.ToString("F2") + ")", costFactorOffset); } } // ------------- TODO: Rot factor ------------- } return(obj); }
//TODO: optimize function internal static FoodSourceRating FoodScoreFor(Policy policy, Pawn eater, Pawn getter, Thing food, bool noDistanceFactor = false, bool quickScore = false) { var obj = new FoodSourceRating(); obj.FoodSource = food; obj.DefRecord = food.GetFoodDefRecord(); // ------ Distance factor ------ bool inInventory = (getter.inventory != null && getter.inventory.innerContainer.Contains(food)); if (!noDistanceFactor) { float distanceFactor; if (inInventory) distanceFactor = 0f; else distanceFactor = -((food.Position - eater.Position).LengthManhattan) * policy.distanceFactor; obj.AddComp("Distance", distanceFactor); } if (inInventory && getter == eater && food.def.ingestible.preferability >= FoodPreferability.MealAwful) { obj.AddComp("Inventory", 500f); } // ------------- Policy food category factor ------------- //if (!policy.unrestricted) // obj.AddComp(food.DetermineFoodCategory().ToString(), policy.GetFoodScoreOffset(eater, food)); // ------------- Prey score factor ------------- const float PREY_FACTOR_MULTIPLIER = 5f; //Because the vanilla prey factor is fairly weak. { if (food is Pawn) { var preyScore = RimWorld.FoodUtility.GetPreyScoreFor(eater, food as Pawn) * PREY_FACTOR_MULTIPLIER; //ducktape, negates the distance factor of the vanilla function as it is already calculated by the mod. preyScore += (eater.Position - food.Position).LengthHorizontal * PREY_FACTOR_MULTIPLIER; obj.AddComp("Prey (ratio=" + FoodUtils.GetPreyRatio(eater, food as Pawn).ToString("F2") + ")", preyScore); return obj; } } // ------------- Food def optimality offset ------------- if (policy.optimalityOffsetMatters && food.def.ingestible != null) { obj.AddComp("Def", food.def.ingestible.optimalityOffset); } if (!quickScore) { // ------------- Mood effect factor ------------- //float num2 = 0f; if (eater.needs != null && eater.needs.mood != null && policy.moodEffectMatters) { List<ThoughtDef> list; if (!(food is RimWorld.Building_NutrientPasteDispenser)) list = RimWorld.FoodUtility.ThoughtsFromIngesting(eater, food); else list = ((RimWorld.Building_NutrientPasteDispenser)food).GetBestMealThoughtsFor(eater); for (int i = 0; i < list.Count; i++) { obj.AddComp(list[i].defName, policy.moodEffectFactor * Detours.Access.FoodOptimalityEffectFromMoodCurve.Evaluate(list[i].stages[0].baseMoodEffect)); } } // ------------- Cost factor (waste factor aswell) ------------- //TODO: concurrent based waste factor. float costRatio; float foodNutrition; var curFoodLevel = eater.needs.food.CurLevel; var maxFoodLevel = eater.needs.food.MaxLevel; if (food.def.IsFoodDispenser) foodNutrition = ThingDefOf.MealNutrientPaste.ingestible.nutrition; else if (food.def.IsCorpse) { var corpse = food as Corpse; BodyPartRecord part = corpse.GetBestBodyPartToEat(eater, curFoodLevel)); if (part != null) foodNutrition = RimWorld.FoodUtility.GetBodyPartNutrition(corpse.InnerPawn, part); } else foodNutrition = food.def.ingestible.nutrition; if (foodNutrition >= 0.1f) { //TODO: ajust; add record for the NPD if (food is RimWorld.Building_NutrientPasteDispenser) costRatio = (food.def.building.nutritionCostPerDispense * 0.05f) / ThingDefOf.MealNutrientPaste.ingestible.nutrition; else costRatio = (obj.DefRecord.costFactor); if (food.def.IsCorpse) costRatio *= 0.25f; //TODO: combine waste & cost factors float actualMealCost = costRatio * foodNutrition; float costFactorOffset = Math.Max(0, actualMealCost - (maxFoodLevel - curFoodLevel)) * -Config.CostFactor /* * policy.costFactorMultiplier*/; //float costFactorOffset = (actualMealCost - (maxFoodLevel - curFoodLevel)) * -Config.CostFactor /* * policy.costFactorMultiplier*/; //* Config.CostFactor * policy.costFactorMultiplier; if (costFactorOffset != 0f) obj.AddComp("Hunger/Cost (min=" + actualMealCost.ToString("F2") + ")", costFactorOffset); } // ------------- TODO: Rot factor ------------- var compRottable = food.TryGetComp<CompRottable>(); if (compRottable != null) { var ticksUntilRotAtCurrentTemp = compRottable.TicksUntilRotAtCurrentTemp; const int V = 30000; if (ticksUntilRotAtCurrentTemp < V) { obj.AddComp("Rotting soon", Config.RottingScoreFactor / (ticksUntilRotAtCurrentTemp - V)); } else { var daysToRotStart = GenDate.TicksToDays(ticksUntilRotAtCurrentTemp); if (daysToRotStart >= 40) { obj.AddComp("Rots in " + daysToRotStart.ToString("####") + " days.", -Mathf.Min(Config.UnrottableFoodScoreOffset * 0.7f, Mathf.Log((1 + daysToRotStart)) * 20)); } } } else { obj.AddComp("Never rots", -Config.UnrottableFoodScoreOffset); } } return obj; }