// 共享类型与常量。所有数值/台词的原文在 src/data/ 下,本文件只放结构性定义。 export type Stage = 0 | 1 | 2 | 3 | 4; /** §6.4 侵蚀阶段矩阵:视听参数唯一真源表 */ export interface StageParams { saturation: number; // ColorMatrix 饱和度 vignette: number; // 晕影强度 0-1 playbackRate: number; // BGM 速率 lowpass: number; // 低通截止 Hz birdMuted: boolean; // 鸟鸣声部静音 textLevel: 0 | 1 | 2 | 3; // 0 无 / 1 标点重复(1/80) / 2 同音错字(1/50) / 3 加密(1/10) negativePerMin: number; // 随机单帧负片频率(次/分钟) petalBright: boolean; // 花瓣过亮 } export const STAGE_PARAMS: Record = { 0: { saturation: 0.90, vignette: 0.00, playbackRate: 1.00, lowpass: 22050, birdMuted: false, textLevel: 0, negativePerMin: 0.0, petalBright: false }, 1: { saturation: 1.00, vignette: 0.00, playbackRate: 1.00, lowpass: 22050, birdMuted: false, textLevel: 0, negativePerMin: 0.0, petalBright: false }, 2: { saturation: 1.15, vignette: 0.00, playbackRate: 0.97, lowpass: 22050, birdMuted: false, textLevel: 1, negativePerMin: 0.0, petalBright: false }, 3: { saturation: 1.30, vignette: 0.30, playbackRate: 0.92, lowpass: 8000, birdMuted: false, textLevel: 2, negativePerMin: 0.0, petalBright: true }, 4: { saturation: 1.40, vignette: 0.60, playbackRate: 0.84, lowpass: 4000, birdMuted: true, textLevel: 3, negativePerMin: 0.4, petalBright: true }, }; /** 由幸福值映射阶段(§6.4) */ export function stageOf(happiness: number): Stage { if (happiness >= 100) return 4; if (happiness >= 81) return 3; if (happiness >= 51) return 2; if (happiness >= 21) return 1; return 0; } export type NpcId = | 'afu' | 'yuanyuan' | 'dabao' | 'mimi' | 'fuyisheng' | 'guiyeye' | 'bird' | 'squirrel' | 'hedgehog' | 'fox' | 'pig' | 'chick'; export interface NpcDef { id: NpcId; name: string; // 对话框说话者名 spriteKey: string; // spritesheet key portraitKey: string; // 头像 key(群演无) offset: number; // §4 阈值偏移(作用于幸福阈值) expressive: boolean; // 是否参与侵蚀表情(芙医生/龟爷爷/群演不参与) isExtra: boolean; // 群演 } export const NPC_DEFS: Record = { afu: { id: 'afu', name: '阿福', spriteKey: 'npc_afu', portraitKey: 'portrait_afu', offset: 0, expressive: true, isExtra: false }, yuanyuan: { id: 'yuanyuan', name: '圆圆', spriteKey: 'npc_yuanyuan', portraitKey: 'portrait_yuanyuan', offset: 15, expressive: true, isExtra: false }, dabao: { id: 'dabao', name: '大宝', spriteKey: 'npc_dabao', portraitKey: 'portrait_dabao', offset: 0, expressive: true, isExtra: false }, mimi: { id: 'mimi', name: '咪咪', spriteKey: 'npc_mimi', portraitKey: 'portrait_mimi', offset: -10, expressive: true, isExtra: false }, fuyisheng: { id: 'fuyisheng', name: '芙医生', spriteKey: 'npc_fuyisheng', portraitKey: 'portrait_fuyisheng', offset: 0, expressive: false, isExtra: false }, guiyeye: { id: 'guiyeye', name: '龟爷爷', spriteKey: 'npc_guiyeye', portraitKey: 'portrait_guiyeye', offset: 0, expressive: false, isExtra: false }, bird: { id: 'bird', name: '双子鸟', spriteKey: 'npc_bird', portraitKey: 'portrait_bird', offset: 0, expressive: false, isExtra: false }, squirrel: { id: 'squirrel', name: '松鼠', spriteKey: 'extra_squirrel', portraitKey: '', offset: 0, expressive: false, isExtra: true }, hedgehog: { id: 'hedgehog', name: '刺猬', spriteKey: 'extra_hedgehog', portraitKey: '', offset: 0, expressive: false, isExtra: true }, fox: { id: 'fox', name: '狐狸', spriteKey: 'extra_fox', portraitKey: '', offset: 0, expressive: false, isExtra: true }, pig: { id: 'pig', name: '小猪', spriteKey: 'extra_pig', portraitKey: '', offset: 0, expressive: false, isExtra: true }, chick: { id: 'chick', name: '小鸡', spriteKey: 'extra_chick', portraitKey: '', offset: 0, expressive: false, isExtra: true }, }; export const NAMED_NPCS: NpcId[] = ['afu', 'yuanyuan', 'dabao', 'mimi', 'fuyisheng', 'guiyeye', 'bird']; export const EXTRA_NPCS: NpcId[] = ['squirrel', 'hedgehog', 'fox', 'pig', 'chick']; /** 地图 key → 场景 key */ export const MAP_SCENES: Record = { house: 'HouseScene', plaza: 'PlazaScene', bakery: 'BakeryScene', shop: 'ShopScene', clinic: 'ClinicScene', lake: 'LakeScene', field: 'FieldScene', well: 'WellScene', fog: 'FogScene', white: 'WhiteScene', }; export const OUTDOOR_MAPS = ['plaza', 'lake', 'field', 'well', 'fog']; /** 对话选项种类(§12:温柔/迟疑/否定;accept/refuse 用于可拒绝任务) */ export type ChoiceKind = 'gentle' | 'hesitant' | 'deny' | 'accept' | 'refuse'; export interface DialogueChoice { label: string; kind: ChoiceKind; } export interface DialoguePayload { speakerId?: NpcId | 'narrator'; lines: string[]; choices?: DialogueChoice[]; } export type SadnessKind = 'refuse' | 'deny' | 'idle' | 'cling' | 'rain'; export type SmallInteractionKind = 'water' | 'feed' | 'pat'; export type EndingId = 'E1' | 'E2' | 'E3' | 'E4'; /** 标准三选项 */ export const STANDARD_CHOICES: DialogueChoice[] = [ { label: '(温柔地回应)', kind: 'gentle' }, { label: '(迟疑)', kind: 'hesitant' }, { label: '(否定)', kind: 'deny' }, ]; export const GAME_W = 320; export const GAME_H = 180; export const MOVE_SPEED = 96; export const INTERACT_RANGE = 24;