I was trying to understand how villagers prioritise eating different foods. From looking at the game code, it seems that the game randomly reorders all the food items into a new priority list every time they restock their inventory, and the villager then goes down that list, picking up as much as possible from the current location, until their inventory is full. [I’m not entirely sure if the same logic is used when a residence sends a logistics request to be stocked with food.]
This means that they might pick up (eg) greens, mushrooms, and preserved vegetables, while ignoring all proteins, fruits, grain, and dairy completely. Considering having a variety of food types is a big part of upgrading houses, this seems counter intuitive to how they should behave, and part of the reason why houses get randomly stuck unable to upgrade at times.
So I decided to try to mod it to see what happened. I kept the same basic logic, but rather than completely randomising the priority, I made sure that it alternated between the different food types (vegetables, fruit, protein, dairy, grain) as far as possible. This means that the top 5 priority items will always cover all the food types, and so also that food items that belong to a type with little variety (eg, the only grains are bread and pastry) are more likely to be at the top of the list than those with lots of variety (like vegetables and proteins).
I left a game running for a year (well developed T5 town with pop almost constant at 1500) to see what the impact would be, here’s the results (it’s the year 43 → 44 that matters)




As expected there’s a big uptick in Grain and Dairy consumption, a moderate drop in Protein and Veg, and Fruit stays the same. These aren’t huge changes (except for Dairy, but I think my herds were growing at the same time), only about 10-20%, because things are still constrained by what a market happens to be stocking at any given time, but it’s still sizeable. I also saw a whole bunch of houses that become eligible to upgrade, because getting the required food diversity is less of a lottery than before.
Here’s the functional part of the code for a Mono mod, I use harmony to effectively override the Villager.randomizedFoodTypes getter. The rest of it implements the partial randomisation described above.
[HarmonyPatch(typeof(Villager), "randomizedFoodTypes", MethodType.Getter)]
public static class PatchedVillager
{
// Prefix that skips the original method altogether
public static bool Prefix(ref List<Item> __result)
{
__result = RandomizeFoodByNutritionTypes();
return false;
}
public static List<Item> RandomizeFoodByNutritionTypes()
{
// Get a random list of food items, but going through the nutritional types in turn
var randomizedFoodItems = new List<Item>();
// Randomise the foods within each type (first making a deep copy)
var foodRemaining = foodItemsByType.ToDictionary(entry => entry.Key, entry => entry.Value.ToList());
foreach (var item in foodRemaining) { item.Value.Randomize(); }
// Build the output item by item
var typesCycle = new List<Villager.FoodType>();
while (foodRemaining.Count > 0)
{
// Pick a type that hasn't been used since the last cycle, starting a new cycle if necessary
if (typesCycle.Count == 0)
{
typesCycle = new List<Villager.FoodType>(foodRemaining.Keys);
typesCycle.Randomize(null);
}
Villager.FoodType currType = typesCycle[0];
typesCycle.RemoveAt(0);
// Pick a food item from that type, if that leaves it empty don't look at it again
randomizedFoodItems.Add(foodRemaining[currType][0]);
foodRemaining[currType].RemoveAt(0);
if (foodRemaining[currType].Count == 0)
{
foodRemaining.Remove(currType);
}
}
return randomizedFoodItems;
}
// The inverse of Villager.foodNameToFoodTypeDict
public static readonly Dictionary<Villager.FoodType, List<Item>> foodItemsByType = new Dictionary<Villager.FoodType, List<Item>>
{
{ Villager.FoodType.foodTypeProtein,
new List<Item>() {new ItemMeat(), new ItemFish(), new ItemSmokedMeat(), new ItemSmokedFish(), new ItemEggs() }
},
{ Villager.FoodType.foodTypeGrain,
new List<Item>() {new ItemBread(), new ItemPastry() }
},
{ Villager.FoodType.foodTypeVegetable,
new List<Item>() { new ItemBeans(), new ItemRootVegetable(), new ItemMushroom(), new ItemPreservedVeg(), new ItemGreens() }
},
{ Villager.FoodType.foodTypeFruit,
new List<Item>() { new ItemBerries(), new ItemNuts(), new ItemFruit(), new ItemPreserves() }
},
{ Villager.FoodType.foodTypeDairy,
new List<Item>() { new ItemMilk(), new ItemCheese() }
},
};
}
Not the flashiest mod by any stretch, and I haven’t played a full game with it yet, but it feels like a general upgrade to villager AI behaviour.