Exact source used by this build. SHA-256: ad3b51b516902b9019aa7ab9197e84b60c9f308de9b7de4e47001edc53d81727.
1 import type { Player, WorldState } from '../../types';
2 import type { ItemDefinition, Quality } from '../items';
3 import { friendshipTier, getFriendship } from './model';
4 import type { DialogueDefinition, Taste, VillagerDefinition, VillagersContent } from './types';
5 const tastes: Taste[] = ['love', 'hate', 'like', 'dislike', 'neutral'];
6 /** Explicit villager tastes beat universal tastes; exact item IDs beat tags. */
7 export function giftTaste(item: ItemDefinition, villager: VillagerDefinition, data: VillagersContent): Taste {
8 for (const lists of [villager.giftTastes, data.universalTastes]) {
9 for (const taste of tastes) if (lists[taste].itemIds.includes(item.id)) return taste;
10 for (const taste of tastes) if (lists[taste].tags.some((tag) => item.tags.includes(tag))) return taste;
11 }
12 return 'neutral';
13 }
14 export function isVillagerBirthday(state: WorldState, villager: VillagerDefinition): boolean { return state.clock.season === villager.birthday.season && state.clock.day === villager.birthday.day; }
15 export function giftPoints(taste: Taste, quality: Quality, birthday: boolean, data: VillagersContent): number { return Math.floor(data.config.tastePoints[taste] * (birthday ? data.config.birthdayMultiplier : 1) * (taste === 'love' || taste === 'like' ? data.config.giftQualityMultipliers[quality] : 1)); }
16 /** The documented fallback order is authored content; every supplied condition must match. */
17 export function resolveVillagerDialogue(state: WorldState, player: Player, villager: VillagerDefinition, data: VillagersContent, taste?: Taste): DialogueDefinition | undefined {
18 const friendship = getFriendship(player, villager.id);
19 const values: DialogueDefinition['conditions'] = { heartTier: friendshipTier(friendship.points, data), season: state.clock.season, weekday: state.clock.weekday, weather: state.weather, location: player.areaId, firstMeeting: !friendship.met, birthday: isVillagerBirthday(state, villager), ...(taste ? { giftTaste: taste } : {}) };
20 const rank = (entry: DialogueDefinition) => { const keys = Object.keys(entry.conditions); const priority = Math.min(...keys.map((key) => data.config.dialogueFallbackOrder.indexOf(key as keyof DialogueDefinition['conditions'])).filter((value) => value >= 0), data.config.dialogueFallbackOrder.indexOf('default')); return priority * 100 - keys.length * 2 - (entry.villagerId ? 1 : 0); };
21 const scripted = new Set([...data.events.flatMap((event) => event.script.flatMap((step) => step.kind === 'dialogue' ? [step.key] : step.kind === 'choice' ? [step.key, ...step.choices.map((choice) => choice.key)] : [])), ...data.mail.map((mail) => mail.key), ...data.quests.map((quest) => quest.key)]);
22 return data.dialogue.filter((entry) => !scripted.has(entry.key) && (!entry.villagerId || entry.villagerId === villager.id) && Object.entries(entry.conditions).every(([key, value]) => values[key as keyof typeof values] === value)).sort((a, b) => rank(a) - rank(b) || a.key.localeCompare(b.key))[0];
23 }
24