Beispiel #1
0
        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);
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #4
0
		//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;
		}