后院 M1 施工切片 v1.0
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 玩到「小寒」节气必看到柿子树下气泡
- [ ] 其他月份 / 节气不显示
- [ ] 关闭时落库仅到日志(
onDismiss把logEntry传给 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 | 月份折算(day → month)边界错位(如 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 切片优先级(若工期不够砍切片)
- 必做:切片 B(郭芙蓉打碎桶)— 感知度极高,成本低(PRD §2 优先级判读 #5)
- 必做:切片 A 视觉钩子(落叶 + 月亮)— 感知度极高,成本极低(PRD §2 优先级判读 #1)
- 强烈推荐:切片 C 听雨灯 / 柿子树下 — 跟 12 个月剧情挂钩(PRD §6)
- 可砍:切片 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 切片,还是先讨论切片本身?