Friendship, gifts and requests without touching the villagers' code: a Godot mod that hooks into dialogue
Eleven villagers, five hearts each, one gift a day and a secret at three hearts. And the mod doesn't change a single line of the villages they live in.

Gotic has eleven named villagers spread across the island: Lía and Ivo in Brumacosta, Sela and Bruno in Caleta Salobre, Mara and Teo in Altomira, Dalia and Olmo in Vega de Poniente, Abel on the pier, Rosa at the farm and Nara at the north-west pen. They used to just say their lines. With the vecinos mod ("Island friends"), every conversation also shows your friendship, from zero to five hearts.
Once a day you can give each of them something. The game picks whatever they like best from what you're carrying: a favourite is worth 3 points, something they like 2, anything else 1. Every so often someone asks for something, say three sardines or a pumpkin, and it stays on screen until you deliver it (4 points). At three hearts they tell you one of the island's secrets; at five, they give you a keepsake. Bruno, for instance, admits that south-west of the islet a sandbar joins it to the shore.
How it's built
The villagers don't belong to this mod: each one lives in their village's mod (or their own: Abel in fishing, Rosa in the farm, Nara in the flock mod). To leave them untouched I added a generic hook to the dialogue system, extensions: a mod registers a function that runs whenever any conversation opens, checks its title and adds text or buttons. vecinos recognises each villager by how the title starts: "SELA, LA PESCADORA" starts with "SELA".
The gifts aren't its own either. It asks every active mod whether it has ingredients() and take_ingredient(): right now fishing and farming answer (plus the stable, with milk and honey, which are stored with the farm). Requests are picked so they can always be fulfilled:
for id in Friends.ORDER:
if not world.modules.has(Friends.FRIENDS[id]["module"]):
continue
for ask in Friends.FRIENDS[id]["asks"]:
if world.modules.has(Friends.source(ask[0])):
options.append([id, ask[0], ask[1]])
Only villagers whose village is switched on can ask, and only for things the active mods can produce: with fishing off, nobody asks for fish. Switch off the village of whoever gave you a request and the request disappears; a new one arrives 3 minutes later. The "day" for gifts comes from the day-and-night mod if it's on; otherwise it's every 10 minutes of play.
What went wrong
The tests passed, but two mutations survived: requests from villagers who aren't there and not removing the extension when the mod is switched off. Every test had all the villages on, so nobody could ever be missing. And the shared "switching a mod off leaves no trace" test counted interactions, markers and buttons, but never looked at conversations.
Now there's a test with the flock and fishing mods off (Nara can't ask for anything and nobody asks for fish), and the shared test also counts dialogue extensions, for this mod and any future one. The "unlimited gifts" mutation, on the other hand, failed straight away.
After this mod the suite stood at 1,093 checks, zero failures.
Still unverified
I haven't tried it on a phone. The points, each villager's tastes and the 3 minutes between requests are my own choices, not measurements: I don't know whether five hearts feel long or short to real players. And villagers still stand in place while you talk to them; friendship doesn't change any of their routines.