№ · 玩法设计 CH.07.27
主页 · 🎲 玩法设计 · 后院 M1 施工切片 v1.0

后院 M1 施工切片 v1.0

📅 2026-07-27 · 📦 46.4 KB · 🎲 玩法设计 · 📝 MD 文档 🆕 14d 🏷️ 剧情🏷️ 玩法设计🏷️ 美术

3 切片 · 每切片 = 1 commit · 可回滚 — 把 P1 拍板的后院 M1「后院会讲故事」三件套展开成可落地的施工蓝图。

触发:用户拍板 D1-D5(见 §0.1),确定 M1 走 3 切片(5 工作日密度高)。 目标:每个切片 = 1 commit / 1 PR / 可回滚 / 不新增 tsc 错误;切片互不阻塞。 范围:仅文档,不动 src/ 下任何文件;不重写 BackyardTab.tsx 编排层。 配套文档:后院玩法补全方案-v1.md(PRD)/ 主厅总览页精简方案-v1.md(格式范本)/ AGENTS.md(项目守则)。 最后更新:2026-07-12 v1.0 起草。


0. 元信息

0.1 已拍板的决策(用户 P1 拍板,不再讨论)

| 决策 | 结果 | | :-: | :--- | | D1 | M1 走 3 切片(5 工作日密度高):M1.1.A 视觉钩子 / M1.1.B 郭芙蓉打碎桶 / M1.1.C 月度主题卡(2 张) | | D2 | 月度主题卡先 2 张:月01 雨水听雨灯 + 月12 小寒柿子树下 | | D3 | M1 微事件只 展示 + 日志data/year1Plan.ts 剧情追踪 | | D4 | 晾架(M2.3)合并到 M1,留在 M2 | | D5 | 美术 lane 暂不排期(M1 全 CSS + 文字,0 美术压力) |

0.2 范围(做 vs 不做)

| 类别 | 内容 | | :-: | :--- | | | 三层分离新增 6 个数据 / 逻辑文件 + 4 个 UI 组件子文件 + BackyardTab.tsx 追加 import / 挂子组件(不重写编排层) | | 不做 | ❌ 不接 data/year1Plan.ts 剧情追踪(D3);❌ 不合并 M2.3 晾架(D4);❌ 不出美术资源(D5);❌ 不动 66+ tsc 旧错误(AGENTS.md §7);❌ 不重写 BackyardTab.tsx 编排层;❌ 不引入 @/ 别名;❌ 不引入新视觉组件族;❌ 不写函数体实现(仅伪代码级别) |

0.3 完成标准

  • npm run lint 通过(无新增 tsc 错误)
  • npm run build 通过
  • 现有游戏功能未破坏(开档 → 进游戏 → 进后院 tab → 玩 1 个周期)
  • 玩家点进后院 tab 至少看到 2 个 M1.1.A 视觉元素 + 1 次 M1.1.B 微事件 + 月 01 / 月 12 玩到对应节气看到 M1.1.C 气泡
  • 每切片独立可回滚(git revert <commit> 后游戏仍可正常运行)

0.4 视觉风格沿用

  • 严格沿用现有写意卷轴 + 朱砂印章 + 拟物化大按钮(TongfuPaper / TongfuSeal / TongfuInkDot / TongfuSectionTitle
  • 0 美术资源依赖(全 CSS 关键帧 + motion/react + 文字 + emoji
  • 不引入新色板(朱砂 #b71c1c / 琥珀 #c5a059 / 米色 #faf4e8 / 蓝 #1e3a5f / 翠绿 jade
  • 沿用 playSweepSound / playClickFeedback 已有音频(新增音频文件)

0.5 硬约束

  • 绝对不动 src/ 下任何已存在文件的内容(只追加 BackyardTab.tsx 的 import + 挂子组件行)
  • 不写实现代码(伪代码级别可以)
  • 严格三层分离(AGENTS.md §1)
  • 玩家可见文案是中文玩家能看的话(AGENTS.md §4 2026-07-12 新增)
  • 命名严格遵守 AGENTS.md §4:数据裸名 / 工具 Logic 后缀 / UI PascalCase + .tsx / 函数 camelCase / 类型 PascalCase / 常量 UPPER_SNAKE_CASE
  • 相对路径,不用 @/ 别名(AGENTS.md §3)

1. 三层分离总览

沿用 auction/ 领域模板,按 AGENTS.md §1 严格分离。新增 = 8 个文件;沿用 = 4 个文件。

1.1 新增文件(8 个)

| 层 | 文件 | 职责 | 类型 / 函数 | 命名守则 | | :-: | :--- | :--- | :--- | :--- | | 数据 | src/data/innAreas/backyardScenery.ts | 4 类视觉元素 + 触发条件 | const + type | 裸名 | | 数据 | src/data/innAreas/backyardMicroEvents.ts | 3 条微事件 + 选项 | const + type | 裸名 | | 数据 | src/data/innAreas/backyardStoryCards.ts | 2 张月度主题卡 | const + type | 裸名 | | 逻辑 | src/utils/innAreas/backyardSceneryLogic.ts | 选景逻辑(按昼夜 / 节气) | function | Logic 后缀 | | 逻辑 | src/utils/innAreas/backyardMicroEventsLogic.ts | 骰子 + 解决逻辑 | function | Logic 后缀 | | 逻辑 | src/utils/innAreas/backyardStoryCardsLogic.ts | 主题卡匹配逻辑 | function | Logic 后缀 | | UI | src/components/BackyardSceneryLayer.tsx | 4 类视觉元素渲染 | .tsx | PascalCase | | UI | src/components/BackyardMicroEventBanner.tsx | 顶部滚动条 + 飞鸽小报按钮 | .tsx | PascalCase | | UI | src/components/BackyardMicroEventCard.tsx | 展开卡:6 行对白 + 2 选 1 | .tsx | PascalCase | | UI | src/components/BackyardStoryCard.tsx | 月度主题卡气泡 + 展开 | .tsx | PascalCase |

注:UI 层 4 个文件 + 1 处 BackyardTab.tsx 追加(不重写)。共 4 新 UI 组件 + 1 改动入口

1.2 沿用文件(4 个,只读不写

| 文件 | 沿用内容 | 本切片用到什么 | | :--- | :--- | :--- | | src/components/BackyardTab.tsx | 主 tab 入口,编排层 | 顶部题头下 / 操作按钮下 / 指南上 = 3 个挂点 | | src/data/innAreas/facilityEffects.ts | AREA_EFFECT_RULES.backyard_yard 数值 | M1 不接数值调整,但场景元素里"扩建 N 级解锁"读 level 字段 | | src/utils/innAreas/facilityLogic.ts | getResourceCapacity / getBackyardProductionMultiplier | M1 不调用(M1 不改数值),但不能动 | | src/utils/gameTime.ts | TimeOfDay / getTimeOfDay / getTimeOfDayLabel / getBusinessPhase / formatGameTime | M1.1.A / C 读 timeOfDay;M1.1.C 读 day(折算月份) | | src/data/staff/index.ts | staff.guo / staff.bai / staff.lu preferredJobs / level / unlocked | M1.1.B 检测"谁在后院" |

1.3 import 方向(单向依赖)

BackyardSceneryLayer / MicroEvent* / StoryCard (UI)
        ↓ depends on
backyardSceneryLogic / MicroEventsLogic / StoryCardsLogic (逻辑)
        ↓ depends on
backyardScenery / MicroEvents / StoryCards (数据) + 沿用的 gameTime / staff

禁止反向依赖(数据层不 import utils/、逻辑层不 import components/)。

1.4 BackyardTab.tsx 挂点(不重写编排层

只追加 4 处(其余 0 改动):

| 挂点 | 位置 | 挂什么 | 切片 | | :-: | :--- | :--- | :-: | | P1 | 题头 div 下方(line 107 之后) | <BackyardSceneryLayer /> + <BackyardStoryCard /> | A + C | | P2 | 左侧"劳作指南"上方(line 209 之前) | <BackyardMicroEventBanner /> | B | | P3 | 左侧"劳作指南"内部(line 210 后) | <BackyardMicroEventCard />(条件渲染) | B | | P4 | 顶部 import 区(line 11-16 后) | 4 个新组件的 import | A + B + C |

关键约束

  • ❌ 不重写 BackyardTab.tsx 已有内容(line 1-318 全部保留)
  • ✅ 只在 P1 / P2 / P3 / P4 这 4 处追加行
  • ✅ P3 条件渲染:仅当 microEvent !== null 才挂

2. 切片 A「后院有风」— M1.1.A 视觉钩子

2.1 目标

玩家感知:点进后院 tab,立刻看到后院"有风有月亮"

  • 白天看到老槐树飘落叶 + 井口泛波纹
  • 晚上看到屋顶挂灯笼 + 月光洒进院子
  • 风铃在有风时响(用 playSweepSound 沿用,无新音频)
  • 全 CSS + motion/react 实现,0 美术资源

50-100 字白话:点进后院,白天能看见老槐树飘落叶、井口波光粼粼;夜晚屋顶灯笼亮着、井边映着月光。风铃偶尔随风响一声——后院不再是个"资源流水线开关",而是一块"有故事的小院子"。

2.2 文件改动清单

| 层 | 文件 | 操作 | 内容概要 | | :-: | :--- | :-: | :--- | | 数据 | src/data/innAreas/backyardScenery.ts | 新增 | 4 类 BackyardSceneryElement + 触发条件 condition + 玩家可见 playerLabel | | 逻辑 | src/utils/innAreas/backyardSceneryLogic.ts | 新增 | 纯函数 pickSceneryElements(time, season, solarTerm) | | UI | src/components/BackyardSceneryLayer.tsx | 新增 | 接收 elements: BackyardSceneryElement[],绝对定位叠加 4 类元素层(CSS 关键帧驱动) | | UI | src/components/BackyardTab.tsx | 追加 import + 挂子组件 | P4 import + P1 挂 <BackyardSceneryLayer /> | | 沿用 | src/utils/gameTime.ts | 只读 | TimeOfDay / getTimeOfDay |

2.3 类型 / 函数签名草案(伪代码级别)

// src/data/innAreas/backyardScenery.ts(数据层 — 仅 const + type)

export type SceneryElementId =
  | 'falling_leaf'   // 落叶
  | 'well_ripple'    // 井波纹
  | 'wind_chime'     // 风铃
  | 'moon_glow';     // 月亮

export type TimeOfDayWindow = 'dawn' | 'day' | 'dusk' | 'night';

export interface SceneryCondition {
  timeOfDay?: TimeOfDayWindow[];   // 触发时段(空 = 全时段)
  solarTerms?: string[];           // 触发节气(空 = 全节气,例如 '立春','雨水')
  excludeSolarTerms?: string[];    // 排除节气
  minYardLevel?: number;           // 至少需要的 backyard_yard 等级(0-3)
}

export interface BackyardSceneryElement {
  id: SceneryElementId;             // 开发者字段(不渲染)
  playerLabel: string;              // 玩家可见:中文短描述,如"老槐树飘落黄叶"
  condition: SceneryCondition;      // 触发条件
  renderHint: {                     // 渲染提示(开发者字段,给 UI 层读)
    layer: 'background' | 'midground' | 'foreground';
    motionKey: 'sway' | 'drift' | 'ripple' | 'glow';
    emoji?: string;                 // emoji 装饰(沿用现有风格)
  };
}

export const BACKYARD_SCENERY_ELEMENTS: ReadonlyArray<BackyardSceneryElement> = [
  // 4 类元素 + 各自的 condition
  // 示例:falling_leaf 在 'day' / 'dawn' 触发;moon_glow 在 'night' 触发
];
// src/utils/innAreas/backyardSceneryLogic.ts(逻辑层 — 仅 function)

import type { BackyardSceneryElement } from '../../data/innAreas/backyardScenery';
import type { TimeOfDay } from '../../types';

export interface SceneryQuery {
  timeOfDay: TimeOfDay;             // 当前时段(从 gameTime 拿)
  solarTerm: string;                // 当前节气(中文,如 '雨水')
  yardLevel: number;                // backyard_yard 等级(0-3)
}

export function pickSceneryElements(query: SceneryQuery): BackyardSceneryElement[];
// 返回当前应当叠加渲染的元素子集
// 算法:遍历 BACKYARD_SCENERY_ELEMENTS,过滤 condition 全部满足的元素
// src/components/BackyardSceneryLayer.tsx(UI 层 — 仅渲染)

import type { BackyardSceneryElement } from '../data/innAreas/backyardScenery';

interface BackyardSceneryLayerProps {
  elements: BackyardSceneryElement[];   // 由逻辑层 pick 出来的当前要渲染的元素
}

export const BackyardSceneryLayer: React.FC<BackyardSceneryLayerProps> = ({ elements }) => {
  // 绝对定位叠加在 BackyardTab 顶部背景
  // 每类元素用不同的 motion/react animate + CSS 关键帧
  // 不写业务计算
};

2.4 commit message 草案

feat(backyard): add 4 scenery layers (leaf / well ripple / wind chime / moon)

- 数据:新增 src/data/innAreas/backyardScenery.ts(4 类元素 + 触发条件)
- 逻辑:新增 src/utils/innAreas/backyardSceneryLogic.ts(pickSceneryElements 纯函数)
- UI:新增 src/components/BackyardSceneryLayer.tsx(绝对定位叠加层)
- UI:BackyardTab.tsx 追加 1 处 import + 1 处挂子组件(其余 0 改动)
- 验证:白天看见落叶 + 井波纹;夜晚看见月亮 + 风铃
- 不影响数值 / 扩建 / 委派逻辑;不新增 tsc 错误

2.5 验收 checklist(切片 A 独立可点选)

  • [ ] npm run lint 无新增 tsc 错误
  • [ ] npm run build 通过
  • [ ] BACKYARD_SCENERY_ELEMENTS 数组包含 4 个元素(falling_leaf / well_ripple / wind_chime / moon_glow)
  • [ ] 每个元素的 playerLabel 是中文玩家能看的话(不暴露 id / solarTerm / yardLevel 等开发者字段)
  • [ ] pickSceneryElements 是纯函数(无副作用,可单测)
  • [ ] 进后院 tab,白天(slot 8-17)能看见至少 2 个视觉元素(落叶 + 井波纹)
  • [ ] 进后院 tab,夜晚(slot 18-23 / 0-5)能看见至少 1 个视觉元素(月亮 + 风铃)
  • [ ] 视觉风格沿用 TongfuPaper / TongfuSeal 调性(不漂移)
  • [ ] BackyardTab.tsx 编排层除 import + 挂子组件外无其他改动(diff 可见)
  • [ ] git revert 此 commit 后游戏仍可正常运行

2.6 风险 + 缓解

| # | 风险 | 等级 | 缓解 | | :-: | :--- | :-: | :--- | | 1 | 4 类视觉元素堆叠导致后院 tab 渲染卡顿(每帧重算) | 中 | pickSceneryElements 加 memo;UI 层用 useMemo 缓存;元素数量上限 4 | | 2 | 视觉元素跟现有 TongfuPaper 调性漂移 | 低 | 严格沿用现有色板(朱砂 / 琥珀 / 米色 / 蓝),不用新色;emoji 沿用 🪵/💧/🍃/🌙 | | 3 | 玩家进入后院 tab 时元素突然"全亮" | 低 | 元素首次出现用 fade-in 600ms;不闪烁 |

2.7 预计工时

  • 1.0 人/天(数据 0.2 + 逻辑 0.2 + UI 0.4 + 验收 0.2)

3. 切片 B「排山倒海——水桶飞了」— M1.1.B 微事件

3.1 目标

玩家感知:郭芙蓉驻后院时,80-120 Tick 后会"打碎水桶";白展堂驻后院时偶尔抓贼;秀才驻后院时念错账

  • 顶部出现一行小字滚动条("今日小事")
  • 点开弹 6 行飞鸽小报卡 + 2 选 1 按钮
  • 选项落库只走日志(不接 data/year1Plan.ts 剧情追踪,遵循 D3)
  • 80-120 Tick(96-144 秒)冷却,避免刷屏

50-100 字白话:郭芙蓉在后院打水,突然一掌"排山倒海"把水桶打飞了——顶部出现一行朱砂小字,点开是 6 行对白卡,2 选 1(原谅她 / 训她两句)。白展堂驻后院偶尔抓贼、秀才念错账也走同样流程,但冷却 80-120 Tick。

3.2 文件改动清单

| 层 | 文件 | 操作 | 内容概要 | | :-: | :--- | :-: | :--- | | 数据 | src/data/innAreas/backyardMicroEvents.ts | 新增 | BACKYARD_MICRO_EVENTS 数组(3 条:郭打碎桶 / 白抓贼 / 秀念错账)+ 每条 2 选 1 选项 | | 逻辑 | src/utils/innAreas/backyardMicroEventsLogic.ts | 新增 | rollBackyardMicroEvent(staffOnSite, ticksSinceLastEvent) + resolveBackyardMicroEvent(eventId, choiceId) | | UI | src/components/BackyardMicroEventBanner.tsx | 新增 | 顶部滚动条(朱砂印章 + 一行 playerLabel + 飞鸽小报按钮) | | UI | src/components/BackyardMicroEventCard.tsx | 新增 | 飞鸽小报卡:6 行对白 + 2 选 1 按钮 + 关闭按钮 | | UI | src/components/BackyardTab.tsx | 追加 import + 挂子组件 | P4 import + P2 挂 Banner + P3 挂 Card(条件渲染) | | 沿用 | src/data/staff/index.ts | 只读 | staff[staffId].preferredJobs.includes('backyard_wood' \|\| 'backyard_water') | | 沿用 | src/utils/gameTime.ts | 只读 | TimeOfDay(白抓贼仅 night 触发) |

3.3 类型 / 函数签名草案(伪代码级别)

// src/data/innAreas/backyardMicroEvents.ts(数据层)

import type { StaffId } from '../../types';

export type MicroEventId =
  | 'guo_breaks_bucket'    // 郭芙蓉打碎桶
  | 'bai_catches_thief'    // 白展堂抓贼
  | 'lu_misreads_account';  // 秀才念错账

export type MicroEventStaffSlot = 'backyard_wood' | 'backyard_water';

export interface MicroEventChoice {
  id: string;                         // 'forgive' / 'scold' / 开发者字段
  playerLabel: string;                // 玩家可见:「原谅她」 / 「训她两句」
  outcome: 'reward' | 'cost' | 'neutral';
  playerFeedback: string;             // 玩家可见:「治安 +1」 / 「治安 -2」
}

export interface BackyardMicroEvent {
  id: MicroEventId;                   // 开发者字段
  playerTitle: string;                // 玩家可见标题:「郭芙蓉:排山倒海——水桶飞了」
  playerBody: string[];               // 玩家可见对白(6 行内)
  triggerStaff: StaffId;              // 触发员工(开发者字段)
  requiredSlot: MicroEventStaffSlot;  // 'backyard_wood' | 'backyard_water'
  requiredTimeOfDay?: TimeOfDayWindow;// 白抓贼仅 night
  cooldownTicks: { min: number; max: number };  // 80-120
  choices: [MicroEventChoice, MicroEventChoice];  // 2 选 1
}

export const BACKYARD_MICRO_EVENTS: ReadonlyArray<BackyardMicroEvent> = [
  // 3 条:郭打碎桶 / 白抓贼 / 秀念错账
];
// src/utils/innAreas/backyardMicroEventsLogic.ts(逻辑层)

import type { BackyardMicroEvent, MicroEventChoice } from '../../data/innAreas/backyardMicroEvents';
import type { Staff, StaffId } from '../../types';

export interface MicroEventQuery {
  staffOnSite: Staff[];                   // 当前在后院的员工
  backyardAllocation: Record<StaffId, 'lobby' | 'backyard_wood' | 'backyard_water'>;
  ticksSinceLastEvent: number;           // 距离上次触发的 Tick 数
  currentTimeOfDay: TimeOfDay;
  lastEventId: MicroEventId | null;
}

export type MicroEventRollOutcome =
  | { kind: 'no_event'; reason: 'cooldown' | 'no_matching_staff' | 'no_eligible_event' }
  | { kind: 'event'; event: BackyardMicroEvent };

export function rollBackyardMicroEvent(query: MicroEventQuery): MicroEventRollOutcome;
// 纯函数:根据 query 返回是否触发、触发哪条
// 算法:找到当前 staffOnSite 中匹配 triggerStaff 且在 backyard_* 任务的员工;
//       检查 lastEventId 触发的 event.cooldownTicks.max 是否已过;
//       从匹配的事件里随机选 1 条返回。

export interface MicroEventResolveResult {
  playerFeedback: string;                // 玩家可见反馈(如「治安 +1」)
  numericChanges?: {                     // 数值变更(仅日志记录,不接剧情追踪)
    securityDelta?: number;
    cleaningDelta?: number;
    staffMoodDelta?: number;
  };
  logEntry: string;                      // 日志条目(中文)
}

export function resolveBackyardMicroEvent(
  eventId: MicroEventId,
  choiceId: string,
): MicroEventResolveResult;
// 纯函数:根据 eventId + choiceId 返回结果
// 玩家选择后写入**当日日志**(不接 data/year1Plan.ts 剧情追踪,遵循 D3)
// src/components/BackyardMicroEventBanner.tsx(UI 层)
interface BackyardMicroEventBannerProps {
  current: BackyardMicroEvent | null;     // null = 隐藏
  onClick: () => void;                    // 打开飞鸽小报
}

// src/components/BackyardMicroEventCard.tsx(UI 层)
interface BackyardMicroEventCardProps {
  event: BackyardMicroEvent;
  onChoose: (choiceId: string) => void;   // 玩家选了
  onClose: () => void;                    // 玩家关闭
}

3.4 commit message 草案

feat(backyard): add 3 micro events (guo breaks bucket / bai catches thief / lu misreads account)

- 数据:新增 src/data/innAreas/backyardMicroEvents.ts(3 条事件 + 2 选 1 选项)
- 逻辑:新增 src/utils/innAreas/backyardMicroEventsLogic.ts
        rollBackyardMicroEvent (纯函数) + resolveBackyardMicroEvent (纯函数)
- UI:新增 BackyardMicroEventBanner.tsx(顶部滚动条 + 飞鸽小报按钮)
- UI:新增 BackyardMicroEventCard.tsx(飞鸽小报卡:6 行对白 + 2 选 1)
- UI:BackyardTab.tsx 追加 1 处 import + 2 处挂子组件(其余 0 改动)
- 冷却:80-120 Tick(遵循 PRD §3 提案 B 频次)
- 落库:仅日志(不接 data/year1Plan.ts 剧情追踪,遵循 D3)
- 验证:郭驻后院 80-120 Tick 后能看到 1 次「排山倒海」事件
- 不影响数值 / 扩建 / 委派逻辑;不新增 tsc 错误

3.5 验收 checklist(切片 B 独立可点选)

  • [ ] npm run lint 无新增 tsc 错误
  • [ ] npm run build 通过
  • [ ] BACKYARD_MICRO_EVENTS 数组包含 3 个事件(guo_breaks_bucket / bai_catches_thief / lu_misreads_account)
  • [ ] 每个事件含 2 选 1 选项(choices 数组长度 === 2)
  • [ ] 每个事件的 playerTitle / playerBody / choiceLabel 是中文玩家能看的话
  • [ ] rollBackyardMicroEvent 是纯函数(无副作用,可单测)
  • [ ] 冷却:同一事件 80-120 Tick 内重复触发
  • [ ] 郭芙蓉驻后院(分配到 backyard_wood 或 backyard_water)80-120 Tick 后触发 1 次 guo_breaks_bucket
  • [ ] 白展堂驻后院 + 夜晚(slot 18-23 / 0-5)80-120 Tick 后触发 bai_catches_thief
  • [ ] 选项结果写入日志(resolveBackyardMicroEvent 返回的 logEntry),data/year1Plan.ts 剧情追踪(D3)
  • [ ] 飞鸽小报卡 6 行对白 + 2 选 1 按钮 + 关闭按钮齐全
  • [ ] 选完 / 关闭后 3 秒内 Banner 自动消失
  • [ ] BackyardTab.tsx 编排层除 import + 挂子组件外无其他改动
  • [ ] git revert 此 commit 后游戏仍可正常运行

3.6 风险 + 缓解

| # | 风险 | 等级 | 缓解 | | :-: | :--- | :-: | :--- | | 1 | 80-120 Tick 频次过密导致玩家烦躁 | 中 | 数据层暴露 cooldownTicks 常量(如 BACKYARD_MICRO_EVENT_COOLDOWN),后续可调 | | 2 | 玩家没注意到 Banner,错失微事件 | 中 | Banner 用朱砂印章(高对比)+ 600ms 抖动进入动画;点 X 关闭前不消失 | | 3 | 选完 / 关闭后微事件重新立即触发 | 低 | 选完 / 关闭时把 ticksSinceLastEvent 重置为 0,强制冷却 | | 4 | 数值变化(治安 / 清洁)写错导致经济崩 | 低 | M1 阶段所有 numericChanges 字段留空undefined),仅写日志;M2 再接数值 |

3.7 预计工时

  • 1.5 人/天(数据 0.3 + 逻辑 0.4 + UI 0.5 + 验收 0.3)
  • 略高于 A,因为要处理 3 条事件 + Banner/Card 双组件

4. 切片 C「听雨灯 / 柿子树下」— M1.1.C 月度主题卡

4.1 目标

玩家感知:玩到月 01 雨水节气,进后院 tab 看到"听雨灯"气泡;玩到月 12 小寒节气,看到"柿子树下白三娘"气泡

  • 气泡用朱砂印章 + 1 行中文提示
  • 点开弹 6 行节气对白 + 关闭按钮
  • 关闭时落库到当日日志(不接剧情追踪,遵循 D3)
  • 仅 2 张主题卡:月 01 雨水 / 月 12 小寒(遵循 D2)

50-100 字白话:月 01 雨水节气进后院,看见井边亮一盏手糊莲花灯——点开是 6 行对白("白展堂手糊的听雨灯,挂井边也能听雨"),关闭时写入日志。月 12 小寒节气进后院,看见两棵柿子树光秃秃——白三娘在树下站着,点开是 6 行小寒对白。其他月份 / 节气不显示。

4.2 文件改动清单

| 层 | 文件 | 操作 | 内容概要 | | :-: | :--- | :-: | :--- | | 数据 | src/data/innAreas/backyardStoryCards.ts | 新增 | BACKYARD_STORY_CARDS 数组(2 张:月 01 雨水 / 月 12 小寒) | | 逻辑 | src/utils/innAreas/backyardStoryCardsLogic.ts | 新增 | getActiveStoryCard(month, solarTerm, dismissedIds) | | UI | src/components/BackyardStoryCard.tsx | 新增 | 朱砂印章 + 1 行提示气泡 + 展开 6 行对白 | | UI | src/components/BackyardTab.tsx | 追加 import + 挂子组件 | P4 import + P1 挂 <BackyardStoryCard /> | | 沿用 | src/utils/gameTime.ts | 只读 | day(折算月份)+ 假设节气枚举(待对齐 data/time/solarTerm) |

:节气枚举可能存在于 src/data/time/solarTerm.ts(未读),M1 阶段若不存在则在数据层用 string 类型占位,M2 再对齐

4.3 类型 / 函数签名草案(伪代码级别)

// src/data/innAreas/backyardStoryCards.ts(数据层)

export type StoryCardMonth = 1 | 12;     // M1 只支持月 01 和月 12
export type StoryCardSolarTerm = '雨水' | '小寒';

export interface BackyardStoryCard {
  id: string;                            // 'rain_water_lantern' / 'minor_cold_persimmon'
  month: StoryCardMonth;
  solarTerm: StoryCardSolarTerm;
  playerTitle: string;                   // 「雨水·听雨灯」
  playerHint: string;                    // 1 行提示:「白展堂手糊的莲花灯,挂在井边」
  playerBody: string[];                  // 6 行对白
  sealLabel: string;                     // 朱砂印章文案:「雨水」 / 「小寒」
  logEntry: string;                      // 关闭时落库的日志条目
}

export const BACKYARD_STORY_CARDS: ReadonlyArray<BackyardStoryCard> = [
  // 2 张:月 01 雨水 + 月 12 小寒
];
// src/utils/innAreas/backyardStoryCardsLogic.ts(逻辑层)

import type { BackyardStoryCard } from '../../data/innAreas/backyardStoryCards';

export interface StoryCardQuery {
  month: number;                         // 1-12(从 gameTime.day 折算)
  solarTerm: string;                     // 当前节气(中文)
  dismissedCardIds: ReadonlyArray<string>; // 本日已关闭的卡片(同日不重复弹)
}

export function getActiveStoryCard(query: StoryCardQuery): BackyardStoryCard | null;
// 纯函数:返回当前月 + 节气下应当显示的主题卡(同日 dismissed 过的不再弹)
// 算法:遍历 BACKYARD_STORY_CARDS,匹配 month + solarTerm,过滤 dismissedCardIds
// src/components/BackyardStoryCard.tsx(UI 层)

interface BackyardStoryCardProps {
  card: BackyardStoryCard;
  onDismiss: (logEntry: string) => void;  // 玩家关闭 → 写日志
}

4.4 commit message 草案

feat(backyard): add 2 monthly story cards (rain water lantern / minor cold persimmon)

- 数据:新增 src/data/innAreas/backyardStoryCards.ts(2 张主题卡)
- 逻辑:新增 src/utils/innAreas/backyardStoryCardsLogic.ts(getActiveStoryCard 纯函数)
- UI:新增 src/components/BackyardStoryCard.tsx(朱砂印章 + 6 行对白)
- UI:BackyardTab.tsx 追加 1 处 import + 1 处挂子组件(其余 0 改动)
- 范围:仅月 01 雨水 + 月 12 小寒(遵循 D2,先 2 张跑通)
- 落库:仅日志(不接 data/year1Plan.ts 剧情追踪,遵循 D3)
- 美术:0 资源(CSS + 文字 + 朱砂印章,遵循 D5)
- 验证:月 01 玩到雨水看到听雨灯气泡;月 12 玩到小寒看到柿子树下气泡
- 不影响数值 / 扩建 / 委派逻辑;不新增 tsc 错误

4.5 验收 checklist(切片 C 独立可点选)

  • [ ] npm run lint 无新增 tsc 错误
  • [ ] npm run build 通过
  • [ ] BACKYARD_STORY_CARDS 数组包含 2 张 卡片(rain_water_lantern / minor_cold_persimmon)
  • [ ] 每张卡片含 6 行对白 + 朱砂印章 + 日志条目
  • [ ] 每张卡片的 playerTitle / playerHint / playerBody / sealLabel 是中文玩家能看的话
  • [ ] getActiveStoryCard 是纯函数(无副作用,可单测)
  • [ ] 月 01 玩到「雨水」节气看到听雨灯气泡
  • [ ] 月 12 玩到「小寒」节气看到柿子树下气泡
  • [ ] 其他月份 / 节气显示
  • [ ] 关闭时落库到日志(onDismisslogEntry 传给 BackyardTab),data/year1Plan.ts 剧情追踪(D3)
  • [ ] 同日已 dismissed 的卡片不重复弹
  • [ ] 视觉风格沿用 TongfuSeal 调性(朱砂印章 + 卷轴底)
  • [ ] BackyardTab.tsx 编排层除 import + 挂子组件外无其他改动
  • [ ] git revert 此 commit 后游戏仍可正常运行

4.6 风险 + 缓解

| # | 风险 | 等级 | 缓解 | | :-: | :--- | :-: | :--- | | 1 | 节气枚举不统一(data/time/ 可能没有 solarTerm.ts) | 中 | M1 阶段数据层用 string 占位;M2 再对齐 | | 2 | 玩家玩不到月 01 / 月 12 看不到效果,验收困难 | 中 | 提供一个调试入口(仅 dev 环境):在 BackyardTab 顶部加个隐藏按钮「切换到月 01 / 月 12」;生产构建import.meta.env.DEV 包裹 | | 3 | 主题卡跟现有剧情系统文案不一致 | 低 | 文案严格引用 docs/story/主线故事/月01-开张适应期-节气增强版.md 第 49-51 行 + 月12-岁末收束期-节气增强版.md 第 166-172 行 | | 4 | 月份折算(daymonth)边界错位(如 30 / 31 天) | 低 | 用 Math.ceil(day / 30) 折算;M2 跟 data/time/ 真实月长对齐 |

4.7 预计工时

  • 1.0 人/天(数据 0.3 + 逻辑 0.2 + UI 0.3 + 验收 0.2)

5. 切片依赖关系图

5.1 依赖矩阵

| 切片 | 依赖其他切片 | 依赖沿用代码 | 阻塞其他切片 | | :-: | :--- | :--- | :-: | | A 后院有风 | ❌ 无 | gameTime.ts (TimeOfDay) | ❌ 不阻塞 B / C | | B 排山倒海 | ❌ 无 | staff/index.ts + gameTime.ts (TimeOfDay) | ❌ 不阻塞 A / C | | C 听雨灯 | ❌ 无 | gameTime.ts (day → month) | ❌ 不阻塞 A / B |

关键结论:3 切片完全独立。任何切片没做完,另外两个切片都能写进去。

5.2 沿用代码的依赖(所有切片都不修改

┌──────────────────┐
│  gameTime.ts     │ ←── A 读 TimeOfDay / B 读 TimeOfDay / C 读 day
│  (只读)          │
└──────────────────┘
┌──────────────────┐
│  staff/index.ts  │ ←── B 读 preferredJobs + level + unlocked
│  (只读)          │
└──────────────────┘
┌──────────────────┐
│  BackyardTab.tsx │ ←── A / B / C 各自追加 import + 挂子组件
│  (追加,不重写)   │     4 处挂点:P1 / P2 / P3 / P4
└──────────────────┘
┌──────────────────┐
│ facilityEffects  │ ←── M1 阶段**不**调用(M1 不改数值)
│  + facilityLogic │     M1 不阻塞;M2 写"扩建 N 级解锁晾架"时调用
│  (只读, 本切片不触)│
└──────────────────┘

5.3 切分粒度判断

| 切片 | 文件数 | 改动行数(估) | 粒度评级 | | :-: | :-: | :-: | :-: | | A | 3 新 + 1 改 | ~250 | ✅ 单 commit 合适 | | B | 4 新 + 1 改 | ~350 | ✅ 单 commit 合适 | | C | 3 新 + 1 改 | ~250 | ✅ 单 commit 合适 |

3 切片粒度均匀,没有巨型切片


6. 推荐施工顺序

单开发者串行排期。3 切片可以并行(不同开发线程),但本排期按"单线程 + 留 buffer"给。

6.1 5 工作日排期

| 工作日 | 上午 | 下午 | 交付物 | | :-: | :--- | :--- | :--- | | D1 | A 数据(backyardScenery.ts) | A 逻辑(backyardSceneryLogic.ts) | 切片 A 数据 + 逻辑 commit | | D2 | A UI(BackyardSceneryLayer.tsx) + BackyardTab 挂点 | A 验收(lint + build + 手动进后院看效果) | 切片 A 完整 commit(数据 + 逻辑 + UI 合并或拆 2 commit) | | D3 | B 数据(backyardMicroEvents.ts) | B 逻辑(backyardMicroEventsLogic.ts) | 切片 B 数据 + 逻辑 commit | | D4 | B UI(BackyardMicroEventBanner.tsx + BackyardMicroEventCard.tsx) + BackyardTab 挂点 | B 验收(lint + build + 手动触发 3 条事件) | 切片 B 完整 commit | | D5 | C 数据 + 逻辑 + UI(合并 1 commit) + BackyardTab 挂点 | C 验收 + 全局回归(3 切片都在游戏里同时跑) | 切片 C commit + 验收 checklist 通过 |

总工时:3.5 人/天(A 1.0 + B 1.5 + C 1.0)→ 5 工作日有 1.5 天 buffer 给回归 / 微调。

6.2 并行 vs 串行

| 场景 | 排期 | | :--- | :--- | | 单开发者 | 严格串行(按上表) | | 双开发者 | A + C 一起(D1-D2 出 A 数据 / C 数据;D3-D4 出 A UI / C UI + 验收;D5 给 B 全量) | | 三开发者 | A / B / C 完全并行(D1-D3 各出数据 + 逻辑;D4 各出 UI;D5 联合回归) |

6.3 切片优先级(若工期不够砍切片)

  1. 必做:切片 B(郭芙蓉打碎桶)— 感知度极高,成本低(PRD §2 优先级判读 #5)
  2. 必做:切片 A 视觉钩子(落叶 + 月亮)— 感知度极高,成本极低(PRD §2 优先级判读 #1)
  3. 强烈推荐:切片 C 听雨灯 / 柿子树下 — 跟 12 个月剧情挂钩(PRD §6)
  4. 可砍:切片 A 的风铃 + 井波纹 2 个元素 — 只做落叶 + 月亮就够用

最坏情况:A 只做 2 类元素(落叶 + 月亮) + B 全量 + C 全量 = 4-4.5 天可压下。


7. 验收 checklist 总表(合并 3 切片)

7.1 代码质量(所有切片通用)

  • [ ] npm run lint 通过(tsc --noEmit 无新增 错误;现有 66+ 旧错误保持不变)
  • [ ] npm run build 通过
  • [ ] 无新增 any 类型
  • [ ] 所有新增 .tsx 组件 PascalCase + .tsx 后缀
  • [ ] 所有新增工具文件 *Logic.ts 后缀
  • [ ] 所有新增数据文件无后缀(裸名)
  • [ ] 所有常量 UPPER_SNAKE_CASE
  • [ ] 所有函数 camelCase
  • [ ] 所有类型 PascalCase
  • [ ] 所有 import 用相对路径(无 @/ 别名)

7.2 三层分离(所有切片通用)

  • [ ] 数据层文件 function / export function(grep 验证)
  • [ ] 逻辑层文件 新增 const 配置(除派生索引)
  • [ ] UI 层文件 业务计算(如 const x = a * 0.5
  • [ ] 数据层 import utils/components/
  • [ ] 逻辑层 import components/
  • [ ] UI 层 import 数据层配置(可 import type 拿类型)

7.3 玩家文案(所有切片通用)

  • [ ] 所有 playerTitle / playerBody / playerLabel / playerHint 是中文玩家能看的话
  • [ ] 玩家可见 UI 渲染 id / key / staffId / tick / solarTerm / yardLevel 等开发者字段
  • [ ] 玩家可见 UI 暴露 backyard_yard / backyard_wood / backyard_water / staff.guo / staff.bai 等内部 ID
  • [ ] 无 3 段以上 / / -> / => 平铺的开发者拼接串

7.4 切片 A 验收(合并自 §2.5)

  • [ ] 4 类视觉元素齐全(falling_leaf / well_ripple / wind_chime / moon_glow)
  • [ ] 白天看见至少 2 个元素
  • [ ] 夜晚看见至少 1 个元素
  • [ ] 视觉风格沿用 TongfuPaper 调性
  • [ ] BackyardTab.tsx 编排层除 import + 挂子组件外无其他改动

7.5 切片 B 验收(合并自 §3.5)

  • [ ] 3 条微事件齐全(guo_breaks_bucket / bai_catches_thief / lu_misreads_account)
  • [ ] 每条 2 选 1 选项(choices 长度 === 2)
  • [ ] 80-120 Tick 冷却生效
  • [ ] 郭驻后院 80-120 Tick 后必触发 1 次
  • [ ] 白夜驻后院 + 夜晚会触发
  • [ ] 选项结果仅写日志,不接 data/year1Plan.ts 剧情追踪(D3)
  • [ ] 飞鸽小报卡 6 行对白 + 2 选 1 + 关闭按钮齐全
  • [ ] 选完 / 关闭后 3 秒内 Banner 自动消失

7.6 切片 C 验收(合并自 §4.5)

  • [ ] 2 张主题卡齐全(rain_water_lantern / minor_cold_persimmon)
  • [ ] 月 01 雨水节气看到听雨灯气泡
  • [ ] 月 12 小寒节气看到柿子树下气泡
  • [ ] 其他月份 / 节气不显示
  • [ ] 关闭时仅写日志,不接剧情追踪(D3)
  • [ ] 同日 dismissed 卡片不重复弹
  • [ ] 朱砂印章 + 卷轴底视觉风格

7.7 BackyardTab.tsx 改动范围(所有切片通用)

  • [ ] 改动行数 ≤ 10(4 处追加:P1 / P2 / P3 / P4)
  • [ ] 改动只发生在以下位置:
    • 顶部 import 区(line 11-16 之后)
    • 题头 div 下方(line 107 之后)
    • 左侧"劳作指南"上方(line 209 之前)
    • 左侧"劳作指南"内部(line 210 后,条件渲染)
  • [ ] diff 可见:编排层其他代码 0 改动

7.8 回滚验证(所有切片通用)

  • [ ] 切片 A commit git revert 后游戏可正常运行
  • [ ] 切片 B commit git revert 后游戏可正常运行
  • [ ] 切片 C commit git revert 后游戏可正常运行
  • [ ] 3 commit 全部 revert 后游戏可正常运行(= 回到 M1 前状态)

7.9 手动测试场景

npm run dev
# 1. 开新档 → 进游戏
# 2. 进后院 tab
# 3. 白天:应看到落叶 + 井波纹(A 验证)
# 4. 夜晚:应看到月亮 + 风铃(A 验证)
# 5. 把郭芙蓉分配到劈柴 → 等 80-120 Tick(约 96-144 秒)→ 顶部出现「排山倒海」滚动条(B 验证)
# 6. 点开飞鸽小报卡 → 2 选 1 → 关闭 → Banner 3 秒内消失(B 验证)
# 7. 用调试入口(import.meta.env.DEV)切到月 01 雨水 → 应看到听雨灯气泡(C 验证)
# 8. 切到月 12 小寒 → 应看到柿子树下气泡(C 验证)
# 9. 切到月 05 立夏 → 不应看到任何主题卡气泡(C 验证)

8. 玩家文案

全部是中文玩家能看的话,不含任何 id / key / staffId / tick / solarTerm / yardLevel 等开发者字段。

8.1 切片 A「后院有风」玩家文案

场景 1:白天(slot 8-17)

院子里的老槐树飘下一片黄叶——风铃响了一下。 井口泛着光,涟漪一圈一圈地荡开。

场景 2:夜晚(slot 18-23 / 0-5)

月光从屋顶洒下来,灯笼在风里轻轻晃。 井边映着月色——老槐树的影子落在井台上。

场景 3:雨水节气(白天 + 夜晚)

屋檐在滴水,井口冒着小气泡——风铃响得更勤了。

场景 4:立春 / 谷雨节气(白天)

老槐树冒出嫩芽,新叶在风里抖了抖。

8.2 切片 B「排山倒海」玩家文案

事件 1:郭芙蓉打碎桶

滚动条:后院出了点动静——郭芙蓉又闯祸了。

飞鸽小报卡: 郭芙蓉:排——山——倒——海!……咦,水桶呢? 白展堂(无奈):郭姑娘,你这是练掌还是打水? 佟湘玉(叹气):这一桶水得重新挑……

选项 A(原谅她):算啦——下次小心。 ↳ 反馈:郭芙蓉心情 +5。后院照常。

选项 B(训她两句):郭姑娘,你这一掌劈出去,水桶能飞,菜钱也得飞。 ↳ 反馈:郭芙蓉心情 -3。后院照常。

事件 2:白展堂抓贼(仅夜晚)

滚动条:院墙有动静——白展堂追出去了。

飞鸽小报卡: 白展堂(从屋顶跳下):葵花点穴手——!站住! 黑影(求饶):大侠饶命!我就是路过……蹭顿饭。 莫小贝(探头):老白又抓贼?这次是第几个了?

选项 A(放他走):算了——给碗饭赶走。 ↳ 反馈:治安 +1。名节 +1。

选项 B(送衙门):送邢捕头吧——让他登记。 ↳ 反馈:治安 +2。铜钱 -2(衙门手续费)。

事件 3:秀才念错账(白天)

滚动条:账房那边算错了——吕秀才挠着头。

飞鸽小报卡: 吕秀才(推眼镜):这账……昨天收了三十文……不对,是十三文? 郭芙蓉(撇嘴):秀才你这算的——昨天买的醋钱都能算成酒钱。 佟湘玉(叹气):子曾经曰过——算账不能马虎。

选项 A(帮他重算):来,额陪你对一遍。 ↳ 反馈:账目修正。秀才心情 +3。

选项 B(让他自己改):子曰——自己算错了自己改。 ↳ 反馈:秀才心情 -2。账目最终修正。

8.3 切片 C「听雨灯 / 柿子树下」玩家文案

主题卡 1:月 01·雨水·听雨灯

朱砂印章:雨水

气泡(1 行提示):后院井边亮了一盏灯——白展堂手糊的。

展开对白(6 行): 佟湘玉:这灯……你糊的? 白展堂(别过脸):随便糊的。雨水要放听雨灯——镇东没河,井边也能放。 佟湘玉:额在汉中放听雨灯,许的愿是"找个好地方开客栈"。 白展堂:……实现了? 佟湘玉:实现了。就是这儿的井有点深,灯放不下去。 白展堂:挂门口吧——挂门口比放井里亮。

日志条目:雨水节气——后院井边挂了白展堂手糊的听雨灯。

主题卡 2:月 12·小寒·柿子树下

朱砂印章:小寒

气泡(1 行提示):后院柿子树下——有人站着。

展开对白(6 行): 佟湘玉:那是谁? 白展堂(沉默):……我娘。 白三娘(转身):展堂。小寒了——额来看看你的客栈。 白展堂:娘。 白三娘(看后院):这个客栈——比你以前待过的任何地方都好。 白展堂(眼眶红了):嗯。是家了。

日志条目:小寒节气——白三娘到访后院柿子树下。


9. 不在范围 / 风险

9.1 明确不在本切片范围(划走清单)

| # | 不做的事 | 原因 | 出处 | | :-: | :--- | :--- | :--- | | 1 | ❌ 不接 data/year1Plan.ts 剧情追踪 | 用户 D3 拍板只展示 + 日志 | P1 D3 | | 2 | ❌ 不合并 M2.3 晾架到 M1 | 用户 D4 拍板 M2 再上 | P1 D4 | | 3 | ❌ 不出美术资源(背景图 / 主题卡图) | 用户 D5 拍板 0 美术压力 | P1 D5 | | 4 | ❌ 不重写 BackyardTab.tsx 编排层 | 只在 4 处追加(详见 §1.4) | AGENTS.md §7 | | 5 | ❌ 不动 App.tsx / MainHallPanel.tsx / Sidebar.tsx 等其他 tab | 范围限定后院 | PRD §0.2 | | 6 | ❌ 不修 66+ tsc 旧错误 | AGENTS.md §7 第 7 条 | AGENTS.md §7 | | 7 | ❌ 不引入新视觉组件族 / 色板 / 字体 | 严格沿用 TongfuPaper / TongfuSeal / TongfuInkDot | AGENTS.md §7 | | 8 | ❌ 不引入对 @/ 别名的依赖 | AGENTS.md §3 | AGENTS.md §3 | | 9 | ❌ 不写实现代码(仅伪代码级别) | 本切片是设计文档,不落地 | PRD §0.2 | | 10 | ❌ 不接数值变更(M1 微事件选项不写治安 / 清洁) | 降低经济崩风险 | §3.6 风险 4 | | 11 | ❌ 不做 12 张月度主题卡(仅 2 张) | 用户 D2 拍板先 2 张跑通 | P1 D2 | | 12 | ❌ 不做 8 类后院突发事件(提案 B 全量) | M1.1.B 仅 3 条员工专属 | PRD §3 提案 B |

9.2 风险清单

| # | 风险 | 等级 | 现状 | 缓解 | | :-: | :--- | :-: | :--- | :--- | | 1 | 3 切片都改 BackyardTab.tsx,commit 冲突 | 中 | 4 个挂点 + 1 处 import 改动 | 切片 A / B / C 分先后 commit(A 先 → B → C),每次 import 都插在 import 区末尾,diff 不重叠 | | 2 | 节气枚举不统一data/time/ 可能没有 solarTerm.ts) | 中 | M1 阶段数据层用 string 占位 | M2 再对齐;M1 接受 string 临时类型 | | 3 | 月份折算边界(30 / 31 天不统一) | 低 | M1 用 Math.ceil(day / 30) | M2 跟 data/time/ 真实月长对齐 | | 4 | 微事件频次调错(80-120 Tick 太密 / 太疏) | 中 | 数据层暴露 cooldownTicks 常量 | 上线后看 7 日数据再调;预留 BACKYARD_MICRO_EVENT_COOLDOWN 配置入口 | | 5 | 视觉元素性能开销(每帧重算) | 中 | pickSceneryElements 加 memo;UI 层 useMemo | 元素数量上限 4;不用复杂粒子系统 | | 6 | 66+ tsc 旧错误扩散 | 高 | 项目技术债 | AGENTS.md §7 第 7 条,不新增 / 不主动修;M1 切片完成后 npm run lint 跑一次确认无新增 | | 7 | 玩家文案暴露开发者字段 | 中 | 数据层 id / key 等字段可能被误渲染 | 所有 UI 组件只读 playerTitle / playerBody / playerLabel / playerHint / sealLabel不读 id / key / triggerStaff / cooldownTicks | | 8 | 3 切片同时挂 P1 视觉层位置冲突 | 低 | A(SceneryLayer)绝对定位 + C(StoryCard)气泡层不冲突 | A 用 absolute + pointer-events: none;C 用 absolute + 气泡 z-index 较高 | | 9 | 节气对白跟剧情原文不一致 | 低 | 月 01 / 月 12 故事已有 6 行对白可用 | C 切片文案严格引用 docs/story/主线故事/月01-开张适应期-节气增强版.md 第 49-51 行 + 月12-岁末收束期-节气增强版.md 第 166-172 行 | | 10 | D5 美术 0 排期导致 M2 时美术跟不上 | 低 | M1 跑通用 CSS fallback | M2 阶段把视觉元素接入 scene-pack-backyard 缺口清单(已在 docs/art/asset-gap-list.md 标记) |

9.3 后续 Phase 预告(不在本切片范围)

| Phase | 范围 | 设计依据 | 预计工期 | | :---: | :--- | :--- | :---: | | M2.1 葵花点穴桩 | 白展堂练功场完整版 | PRD §3 提案 A | 3-4 天 | | M2.2 今日小事 | 8 类后院突发事件 + 飞鸽小报 | PRD §3 提案 B | 3-4 天 | | M2.3 同福晾架 | 3 类物品晾干循环 | PRD §3 提案 D | 2-3 天 | | M3.1 节气挂历 | 6 个节气背景 + 季节加成 | PRD §3 提案 C | 5-7 天 | | M3.2 月度剧场 | 12 个月剧情氛围 | PRD §3 提案 E 全量 | 5-7 天 | | M3.3 后院菜畦 + 鸡窝 | 3 种菜 + 1 种鸡(柴鸡蛋) | PRD §2 缺失点 #8 | 5-7 天(依赖 Phase 6 厨房食材系统) |


附录 A. 用户原话存档(避免跑偏)

用户原话(2026-07-12 拍板 D1-D5):

  • D1:「M1 走 3 切片(5 天密度高)」
  • D2:「月度主题卡先 2 张(雨水 / 小寒)」
  • D3:「M1 微事件只展示 + 日志,不接 year1Plan.ts 剧情追踪」
  • D4:「晾架(M2.3)不合并到 M1」
  • D5:「美术 lane 暂不排期(M1 全 CSS + 文字,0 美术压力)」

翻译为施工约束

  • 3 切片 = 视觉钩子(A)+ 郭打碎桶(B)+ 月度卡 2 张(C)
  • 微事件不接剧情追踪 = 仅展示 + 日志落库
  • 晾架 = M2 排期
  • 美术 = 0 资源,全 CSS + 文字

附录 B. 不在本切片范围(明确划走)

  • ❌ 接 data/year1Plan.ts 剧情追踪(用户 D3 拍板只展示 + 日志)
  • ❌ M2.3 晾架(用户 D4 拍板 M2 上)
  • ❌ 美术资源 / 主题卡背景图(用户 D5 拍板 0 美术压力)
  • ❌ 重写 BackyardTab.tsx 编排层(仅 4 处追加)
  • ❌ 动 App.tsx / MainHallPanel.tsx / Sidebar.tsx 等其他 tab
  • ❌ 修 66+ tsc 旧错误(AGENTS.md §7 第 7 条)
  • ❌ 引入新视觉组件族 / 色板 / 字体
  • ❌ 引入对 @/ 别名的依赖
  • ❌ 写实现代码(本切片是设计文档,仅伪代码级别)
  • ❌ 微事件选项接数值变更(M1 仅写日志)
  • ❌ 12 张月度主题卡(仅 2 张:雨水 / 小寒)
  • ❌ 8 类后院突发事件(仅 3 条员工专属微事件)

附录 C. 相关文档(避免跑偏)

| 文档 | 路径 | 用途 | | :--- | :--- | :--- | | 项目守则 | AGENTS.md | 三层分离 + 命名 + 不做的事 | | M1 PRD | docs/design/active/后院玩法补全方案-v1.md | §0-§9 设计源头 | | 格式范本 | docs/design/active/主厅总览页精简方案-v1.md | 本切片格式参考 | | 月01 雨水剧情 | docs/story/主线故事/月01-开张适应期-节气增强版.md | 切片 C 听雨灯文案来源 | | 月12 小寒剧情 | docs/story/主线故事/月12-岁末收束期-节气增强版.md | 切片 C 柿子树下文案来源 | | 现有后院 UI | src/components/BackyardTab.tsx | 主入口(追加 4 处挂点) | | 现有后院扩建数据 | src/data/innAreas/facilityEffects.ts | AREA_EFFECT_RULES.backyard_yard(M1 不动) | | 现有后院逻辑 | src/utils/innAreas/facilityLogic.ts | getBackyardProductionMultiplier(M1 不动) | | 现有员工数据 | src/data/staff/index.ts | 郭 / 白 / 秀 preferredJobs | | 时间系统 | src/data/time/ + src/utils/gameTime.ts | 昼夜 / 节气 / Tick API | | 资源缺口清单 | docs/art/asset-gap-list.md | scene-pack-backyard 待补(M2 接入) |


后院 M1 施工切片 v1.0 完。 等你拍板开 M1.1.A 切片,还是先讨论切片本身?