diff --git a/.docs/plans/2026-03-21-tower-defense-game.md b/.docs/plans/2026-03-21-tower-defense-game.md new file mode 100644 index 0000000..5eac6be --- /dev/null +++ b/.docs/plans/2026-03-21-tower-defense-game.md @@ -0,0 +1,239 @@ +# 《大厂保卫战:最后的打工人》- 任务计划 v1.0 +**日期:** 2026-03-21 +**目标:** 在 Next.js 项目中,用 Phaser.js 实现完整的第一关塔防游戏 +**设计文档:** `design-system/大厂保卫战/MASTER.md` +**需求文档:** `.docs/prd-tower-defense-game.md` + +--- + +## 功能优先级 + +| 优先级 | 功能 | 说明 | +|--------|------|------| +| P0 | 游戏基础框架 + 主页入口 | Next.js 页面 + Phaser.js 集成 + 地图渲染 | +| P0 | 防御塔系统 | 4种塔的部署、攻击、精力管理 | +| P0 | 怪物波次系统 | 6波怪物 + 路径寻路 + 战斗逻辑 | +| P1 | 周报结算系统 | 第3波后触发的黑话问答弹窗 | +| P1 | 特殊技能与摸鱼机制 | 各塔特殊能力 + 精力恢复 + 咖啡购买 | +| P1 | 胜利/失败结算界面 | 钉钉风格弹窗 + 绩效评级 | +| P2 | 怪物头顶语录飘字 | 100条黑话随机飘过 | + +--- + +## Phase 1(当前批):游戏基础框架 + 主页 + +**目标:** 搭建可运行的 Next.js + Phaser.js 框架,渲染 S 型地图,部署基础塔,实现预设路径怪物移动 + +### 任务列表 + +- [ ] **Task 1A: 安装依赖 + 创建游戏入口页面** + - 批次:当前批 + - 执行方式:串行(基础设施) + - 前置依赖:无 + - 目标: + - 安装 `phaser` npm 包 + - 创建 `app/game/page.tsx`:Next.js 游戏页面(`use client`) + - 创建 `app/page.tsx`:主页(游戏封面 + 进入游戏按钮,覆盖模板原有页面) + - 字体:从 Google Fonts 加载 Press Start 2P + VT323 + - 涉及范围: + - `package.json`(添加 phaser 依赖) + - `app/page.tsx`(主页重写) + - `app/game/page.tsx`(游戏页面新建) + - `app/layout.tsx`(添加字体 import) + - `app/globals.css`(游戏全局样式) + - 验收标准:访问 `/game` 页面不报错,主页显示游戏封面 + - 验证方式:`curl -s http://localhost:13000/game | grep -c "html"` + +- [ ] **Task 1B: Phaser.js 游戏核心框架** + - 批次:当前批 + - 执行方式:串行(依赖 1A) + - 前置依赖:Task 1A 完成 + - 目标: + - 创建 `game/` 目录(游戏源码目录,非 Next.js app 目录) + - 实现 `game/GameScene.ts`:主游戏场景,S型地图渲染(16x12格,格子48px) + - 实现 `game/config.ts`:Phaser 配置(Canvas 1280x720,transparent background) + - 实现 `game/constants.ts`:游戏常量(地图路径坐标、HC初始值200、KPI初始100等) + - 实现 `game/GameManager.ts`:游戏状态管理(HC、KPI、波次、游戏状态) + - 在 `app/game/page.tsx` 中挂载 Phaser Game 实例(useEffect) + - 涉及范围: + - `game/config.ts` + - `game/constants.ts` + - `game/GameScene.ts` + - `game/GameManager.ts` + - `app/game/page.tsx` + - 关键实现细节: + - S型路径坐标:`[(0,2),(7,2),(7,9),(15,9)]`,格子大小 48px + - 地图背景:深色办公室风格(`#0A1628`),路径用深褐色(`#2D1B00`) + - 可建塔区域:路径以外的格子,点击触发建塔UI + - 所有 Phaser 相关代码只在客户端运行(SSR 需跳过) + - 验收标准:浏览器显示游戏地图,S型路径可见,格子可点击 + - 验证方式:启动开发服务器后访问 `/game` + +- [ ] **Task 1C: 防御塔基础系统(部署+攻击+精力)** + - 批次:当前批 + - 执行方式:串行(依赖 1B) + - 前置依赖:Task 1B 完成 + - 目标: + - 实现 `game/towers/TowerBase.ts`:塔基类(位置、成本、攻速、精力、攻击范围) + - 实现 4 种塔: + - `game/towers/InternTower.ts`(00后实习生,50HC,近战) + - `game/towers/SeniorDevTower.ts`(P6资深开发,120HC,远程直线) + - `game/towers/PPTMasterTower.ts`(PPT大师,100HC,范围减速) + - `game/towers/HRBPTower.ts`(HRBP,80HC,辅助Buff) + - 实现 `game/towers/TowerManager.ts`:塔的部署管理、攻击目标选择 + - HUD 底部:塔选择栏(4个卡片,显示名称、成本、当前可选状态) + - 点击地图格子后弹出确认浮层,扣除 HC 后放置塔 + - 塔的精力条显示(进度条悬浮在塔上方) + - 涉及范围: + - `game/towers/` 目录(5个文件) + - `game/GameScene.ts`(集成塔管理器) + - `game/ui/TowerPanel.ts`(底部塔选择 HUD) + - 验收标准:可以点击格子选塔,扣除 HC 后塔出现在地图上,塔有精力条 + - 验证方式:手动游玩验证 + +- [ ] **Task 1D: 怪物波次系统(路径寻路+战斗)** + - 批次:当前批 + - 执行方式:串行(依赖 1B) + - 前置依赖:Task 1B 完成(可与 1C 并行) + - 执行方式:并行(P1-游戏核心)(与 1C 并行) + - 关键实现细节: + - 实现 `game/enemies/EnemyBase.ts`:怪物基类(血量、移速、路径跟随、受伤、死亡) + - 实现 4 种怪物: + - `game/enemies/FreshGraduate.ts`(校招应届生:30HP,快速,数量多) + - `game/enemies/OldEmployee.ts`(35岁老员工:150HP,慢速,经验护盾) + - `game/enemies/TroubleMaker.ts`(职业碰瓷王:80HP,死亡扣20HC) + - `game/enemies/BossVP.ts`(空降VP:800HP,组织架构调整技能) + - 实现 `game/enemies/WaveManager.ts`:波次管理器(6波配置、波次间隔、怪物生成) + - 实现路径跟随:沿预设路径坐标数组移动,到达终点扣除 KPI + - 实现攻击目标选择:塔选择射程内最近怪物 + - 对象池:复用怪物对象,避免频繁 GC + - 怪物头顶显示血条 + - 涉及范围: + - `game/enemies/` 目录(6个文件) + - `game/GameScene.ts`(集成波次管理器) + - 验收标准:第1波10只应届生沿S型路径走到终点,KPI 正确扣减 + - 验证方式:手动游玩第1波 + +--- + +## Phase 2(下一批):游戏玩法完整化 + +**目标:** 实现周报结算、特殊技能、完整6波、胜利/失败界面 + +- [ ] **Task 2A: 周报结算系统 + 特殊技能** + - 批次:下一批 + - 执行方式:并行(P2-玩法) + - 前置依赖:Phase 1 全部完成 + - 目标: + - 实现周报弹窗(第3波结束触发,10秒倒计时,3个黑话选项) + - 实现选对/超时/选错的结果逻辑 + - 实现各塔特殊技能:整顿职场、代码屎山、黑话领域、打鸡血 + - 实现摸鱼机制:点击塔花10HC买咖啡补满精力 + - 涉及范围: + - `game/ui/WeeklyReportModal.ts`(周报弹窗) + - `game/data/buzzwords.ts`(黑话题库100条) + - 各塔文件的特殊技能方法 + - 验收标准:第3波后弹出周报,选对获得奖励,特殊技能正常触发 + +- [ ] **Task 2B: 胜利/失败结算界面 + HUD完善** + - 批次:下一批 + - 执行方式:并行(P2-玩法) + - 前置依赖:Phase 1 全部完成 + - 目标: + - 失败结算:仿钉钉退群通知弹窗(KPI归零触发) + - 胜利结算:仿绩效评级界面(6波全部通过) + - 顶部 HUD 完善:KPI 进度条 + HC 数值实时更新 + - BOSS 出现特效:全屏红色闪光 + 警告文字 + - 怪物头顶语录飘字:100条黑话随机显示 + - 涉及范围: + - `game/ui/VictoryModal.ts` + - `game/ui/DefeatModal.ts` + - `game/ui/HUD.ts` + - `game/data/quotes.ts`(100条语录) + +--- + +## Phase 3(收尾批):体验优化 + +- [ ] **Task 3: 视觉polish + 适配优化** + - 批次:收尾批 + - 前置依赖:Phase 2 完成 + - 目标: + - Canvas 缩放适配(不同浏览器窗口大小等比缩放) + - CRT 扫描线效果(CSS叠加层) + - 霓虹发光按钮动效 + - 音效模拟(Web Audio API,可跳过) + - 主页视觉完善(游戏截图预览、特性介绍) + +--- + +## 技术关键点 + +### Phaser.js 在 Next.js 中的集成 +```typescript +// app/game/page.tsx +'use client' +import { useEffect, useRef } from 'react' + +export default function GamePage() { + const gameRef = useRef(null) + + useEffect(() => { + // 动态导入 Phaser(避免 SSR 报错) + import('phaser').then((Phaser) => { + import('@/game/config').then(({ gameConfig }) => { + gameRef.current = new Phaser.Game(gameConfig) + }) + }) + return () => { + gameRef.current?.destroy(true) + } + }, []) + + return
+} +``` + +### 对象池模式 +```typescript +// game/enemies/EnemyPool.ts +class EnemyPool { + private pool: EnemyBase[] = [] + // 从池中获取怪物,没有则创建 + acquire(type: EnemyType): EnemyBase { ... } + // 怪物死亡后回收 + release(enemy: EnemyBase) { ... } +} +``` + +### 游戏数值常量 +```typescript +// game/constants.ts +export const MAP_COLS = 16 +export const MAP_ROWS = 12 +export const TILE_SIZE = 48 +export const CANVAS_WIDTH = MAP_COLS * TILE_SIZE // 768 (内部逻辑宽度,Phaser处理) +export const CANVAS_HEIGHT = MAP_ROWS * TILE_SIZE // 576 + +// S型路径坐标(格子坐标,非像素) +export const PATH_WAYPOINTS = [ + { x: 0, y: 2 }, // 入口(面试间) + { x: 7, y: 2 }, // 第一个转折 + { x: 7, y: 9 }, // 第二个转折 + { x: 15, y: 9 }, // 出口(财务室) +] + +export const INITIAL_HC = 200 +export const INITIAL_KPI = 100 +``` + +--- + +## 相关上下文 + +- PRD 文档:`.docs/prd-tower-defense-game.md` +- 设计系统:`design-system/大厂保卫战/MASTER.md` +- 开发规范:`@rules/dev-best-practices.md` +- 原子提交规范:`@rules/atomic-commit.md` + +完成开发后,按 `@rules/atomic-commit.md` 规范,将变更按逻辑模块分组提交(禁止 `git add .`),确保所有文件均已 commit 后再结束任务。 diff --git a/.docs/prd-tower-defense-game.md b/.docs/prd-tower-defense-game.md new file mode 100644 index 0000000..cf61ca5 --- /dev/null +++ b/.docs/prd-tower-defense-game.md @@ -0,0 +1,205 @@ +# 《大厂保卫战:最后的打工人》- PRD v1.0 + +## 1. 产品概述 + +### 产品定位 +一款2D讽刺类塔防小游戏,以互联网大厂职场文化为背景,用黑色幽默和荒诞策略玩法解构"内卷"现象。游戏嵌入Next.js页面,通过纯前端(Phaser.js)实现,无需安装额外软件即可在浏览器中游玩。 + +### 背景与目标 +- 目标用户:互联网从业者、对大厂文化有共鸣的泛玩家 +- 核心体验:荒诞、黑色幽默、策略对抗、共情压力 +- 解决痛点:让职场压力通过游戏化方式得到释放和自嘲 + +### 游戏基本信息 +- 游戏名称:《大厂保卫战:最后的打工人》(The Last Grinder) +- 类型:2D 讽刺类塔防 (TD) +- 平台:Web (浏览器),嵌入 Next.js 页面 +- 技术实现:Phaser.js(纯前端游戏框架) + +--- + +## 2. 功能需求 + +### 核心数值体系 (P0) + +**HC (Headcount)** - 资源单位(等同于传统塔防的金币) +- 初始值:200 HC +- 获取方式:击败怪物 + 每波结束后的基础奖励 + +**KPI (关键绩效指标)** - 基地生命值 +- 初始值:100% +- 扣减规则:怪物到达终点时按怪物类型扣减 + +**精力值 (Stamina)** - 防御塔持续作战能力 +- 范围:0~100 +- 攻击时消耗,不攻击时每秒恢复5% +- 归零后进入"摸鱼"状态(停止攻击) + +--- + +### 核心功能 (P0) + +#### 1. 第一关地图:人力资源部·降本增效 + +**地图布局** +- 形状:S型办公走廊路径 +- 起点:"面试间"(怪物刷出点) +- 终点:"财务室/茶水间"(KPI扣除点) +- 装饰:代码框、倒下的咖啡杯、"奋斗"横幅 + +**路径设计(格子坐标系,16x12格)** +``` +起点(0,2) → 右行至(7,2) → 转向下至(7,9) → 转向右至(15,9) = 终点 +``` + +#### 2. 防御塔系统 + +| 塔名称 | 成本 | 攻击类型 | 特殊技能 | 攻击力 | 攻速 | +|--------|------|----------|----------|--------|------| +| 00后实习生 | 50 HC | 单体近战 | 整顿职场:5%概率秒杀普通怪;每秒1%概率自动离场 | 15 | 1.5/s | +| P6资深开发 | 120 HC | 远程直线 | 代码屎山:子弹附带持续伤害(10/s,持续3s) | 30 | 1.0/s | +| PPT大师 | 100 HC | 范围减速 | 黑话领域:范围内敌人移速降低40% | 5 | 1.5/s | +| HRBP | 80 HC | 辅助Buff | 打鸡血:周围1格内塔攻速+20%,每秒消耗其额外精力 | 0 | - | + +#### 3. 敌人波次系统 + +| 怪物名称 | 血量 | 移速 | KPI扣减 | HC奖励 | 特性 | +|----------|------|------|---------|--------|------| +| 校招应届生 | 30 | 快 | 2% | 10 HC | 成群结队,数量多 | +| 35岁老员工 | 150 | 慢 | 8% | 30 HC | 经验护盾:无视前3次攻击 | +| 职业碰瓷王 | 80 | 中等 | 5% | 20 HC | 死亡时扣玩家20 HC(劳动仲裁) | +| BOSS: 空降VP | 800 | 慢 | 30% | 150 HC | 技能:组织架构调整(随机摧毁1个防御塔) | + +**波次设计(共6波)** +- 第1波:10只校招应届生 +- 第2波:8只校招应届生 + 3只35岁老员工 +- 第3波:周报结算 → 5只35岁老员工 + 3只职业碰瓷王 +- 第4波:12只校招应届生 + 2只职业碰瓷王 +- 第5波:6只35岁老员工 + 3只职业碰瓷王 +- 第6波(Boss):周报结算 → 1只空降VP + 4只35岁老员工 + +#### 4. 周报结算系统(每3波触发) + +- 触发时机:第3波结束后、第6波开始前 +- 界面:全屏弹出,10秒倒计时 +- 玩法:从3个随机互联网黑话中选择1个正确答案 +- 结果: + - 选对:随机获得50~100 HC 或 全场塔精力补满 + - 超时/选错:全场塔禁锢3秒 + +**黑话题库(示例)** +``` +赋能 / 对齐 / 颗粒度 / 打通 / 闭环 / 抓手 / 生态 / 赛道 / 护城河 / 降维打击 +底层逻辑 / 顶层设计 / 方法论 / 复盘 / 迭代 / 串联 / 拉齐 / 落地 / 沉淀 / 跑通 +MVP / ROI / KOL / ToB / ToC / 私域 / 增量 / 存量 / 心智 / 势能 +``` + +#### 5. 摸鱼回复机制 + +- 塔不攻击时精力自动恢复:+5%/s +- 点击塔消耗10 HC购买"瑞幸咖啡":瞬间补满精力 + +#### 6. 失败结算界面 + +仿钉钉/飞书通知样式弹窗: +``` +[系统通知] 鉴于您近期的表现未能达成业务闭环,经公司管理层研究决定, +对您进行"毕业"处理。请于5分钟内归还工牌,不要带走办公文具。 + +[领取 N+1] (重新开始) [申请仲裁] (返回主页) +``` + +#### 7. 胜利结算界面 + +仿绩效评级样式,显示: +- 最终KPI分数 +- 消灭怪物总数 +- 剩余HC +- 绩效评级:S/A/B/C + +--- + +### 重要功能 (P1) + +#### 8. 怪物头顶语录飘字 + +- 怪物头顶随机显示大厂黑话语录 +- 100条随机语录池 +- 每个怪物显示1条,淡入淡出动画 + +#### 9. Next.js 主页集成 + +- 主页展示游戏封面、游戏名、开始按钮 +- 游戏画面占据主页核心区域 +- 飞书/钉钉质感的极简扁平化UI + +--- + +## 3. 视觉设计规范 + +### 整体视觉风格 +- 扁平化设计,2D像素艺术风格 +- 色调:灰蓝色调("格子间"氛围),配合荧光绿(代码)、橙红(警报) + +### 主题配色方案 +- 主色(Primary):`#1D2D44`(深夜格子间蓝) +- 辅助色(Secondary):`#3E5C76`(办公室灰蓝) +- 强调色(Accent):`#FF4E00`(加班警报红) +- 成功色:`#22C55E`(KPI绿) +- 背景色(Background):`#0A1628`(深夜办公室) +- 文字色(Text):`#E2E8F0` + +### UI质感参考 +- 飞书/钉钉的极简扁平化UI +- 系统通知样式的弹窗 +- 进度条组件(KPI条、精力条) + +--- + +## 4. 终端与体验策略 + +- 终端范围:`adaptive` +- 判定来源:默认策略(优先PC,兼容平板) +- 实施策略:`responsive_layout`,以 1280×720 为基准尺寸,响应式缩放 + +--- + +## 5. 非功能需求 + +### 性能要求 +- 60fps流畅运行(Phaser.js Canvas 渲染) +- 对象池管理怪物和子弹,避免GC卡顿 +- 最多同时30个怪物实体 + +### 技术栈 +- 游戏引擎:Phaser.js 3.x(纯前端,npm安装) +- 集成方式:Next.js Page 中直接挂载 Phaser Game 实例 +- 无后端依赖,纯客户端运行 + +### 兼容性 +- Chrome 90+, Firefox 88+, Safari 14+ +- 最低分辨率:1024×600 + +--- + +## 6. 项目计划 + +### MVP 范围(第一期) +1. Next.js 主页 + 游戏入口 +2. 第一关完整实现(地图+6波怪物+4种塔) +3. 周报结算系统 +4. 失败/胜利结算界面 +5. 怪物头顶飘字语录 + +### 里程碑 +- M1:游戏基础框架 + 地图渲染 + 路径系统 +- M2:防御塔系统 + 怪物系统 + 战斗逻辑 +- M3:周报结算 + 特殊技能 + UI完善 +- M4:主页集成 + 语录库 + 结算界面 + +--- + +## 7. 开放问题 +- 音效资源:是否需要自制音效(当前先用CSS音效模拟或跳过) +- 存档系统:是否需要保存游戏进度(当前不做) +- 第二关:暂不规划,第一关完善后迭代 diff --git a/.docs/tasks/task-1ab-game-foundation.md b/.docs/tasks/task-1ab-game-foundation.md new file mode 100644 index 0000000..ea9e6e8 --- /dev/null +++ b/.docs/tasks/task-1ab-game-foundation.md @@ -0,0 +1,226 @@ +# Task 1A+1B: 游戏基础框架搭建 + +## 任务概要 +在现有 Next.js 项目中,安装 Phaser.js,改造主页(封面),创建游戏页面,实现 Phaser 游戏框架挂载 + S型地图渲染。 + +## 执行步骤 + +### Step 1: 安装 Phaser.js +```bash +pnpm add phaser +``` + +### Step 2: 改造 `app/page.tsx`(主页/游戏封面) +完全重写主页,展示游戏封面。注意:**必须覆盖模板原有 UI,不保留任何模板样式**。 + +设计要求(参考 `design-system/大厂保卫战/MASTER.md`): +- 背景色:`#0F0F23`(深夜宇宙蓝) +- 标题字体:Press Start 2P(像素字体),颜色 `#A78BFA`(霓虹紫) +- 副标题:VT323 字体 +- 主色 CTA 按钮(进入游戏):`#F43F5E` 红色 +- 效果:CRT 扫描线叠加层(`::before` 伪元素,repeating-linear-gradient 细黑线) +- 霓虹发光效果:标题 `text-shadow` 双层发光 +- 装饰性文字:背景中随机排列大厂黑话(低透明度) + +主页内容结构: +``` +[全屏黑色背景] + [CRT扫描线叠加层] + [装饰黑话背景文字] + [中央卡片] + 标题:大厂保卫战 + 副标题:最后的打工人 + 英文副标:THE LAST GRINDER + 描述:保住KPI,战胜空降VP + [开始游戏] 按钮 → 跳转 /game + [底部版权:像素风格] +``` + +### Step 3: 创建 `app/game/page.tsx`(游戏页面) +```typescript +'use client' +import { useEffect, useRef } from 'react' + +export default function GamePage() { + const gameRef = useRef(null) + + useEffect(() => { + let game: any = null + + const initGame = async () => { + const Phaser = (await import('phaser')).default + const { createGameConfig } = await import('@/game/config') + + if (gameRef.current && !game) { + game = new Phaser.Game(createGameConfig('game-container')) + } + } + + initGame() + + return () => { + game?.destroy(true) + } + }, []) + + return ( +
+
+
+ ) +} +``` + +### Step 4: 创建游戏核心文件 + +**`game/constants.ts`**(游戏常量): +```typescript +export const MAP_COLS = 16 +export const MAP_ROWS = 12 +export const TILE_SIZE = 80 // 每格80px,总计 1280x960,Phaser会缩放 +export const GAME_WIDTH = 1280 +export const GAME_HEIGHT = 720 + +// S型路径格子坐标 +export const PATH_WAYPOINTS = [ + { x: 0, y: 2 }, + { x: 11, y: 2 }, + { x: 11, y: 9 }, + { x: 15, y: 9 }, +] + +// 游戏初始数值 +export const INITIAL_HC = 200 +export const INITIAL_KPI = 100 +export const STAMINA_MAX = 100 +export const STAMINA_REGEN = 5 // 每秒恢复量 +export const COFFEE_COST = 10 // 瑞幸咖啡 HC 成本 + +// 颜色常量 +export const COLOR_PATH = 0x3d2b1f +export const COLOR_BUILDABLE = 0x1e3a5f +export const COLOR_HOVER = 0x2d5a8e +export const COLOR_BORDER = 0x0a1628 +``` + +**`game/config.ts`**(Phaser 配置): +```typescript +import Phaser from 'phaser' +import { GAME_WIDTH, GAME_HEIGHT } from './constants' + +export function createGameConfig(containerId: string): Phaser.Types.Core.GameConfig { + return { + type: Phaser.AUTO, + width: GAME_WIDTH, + height: GAME_HEIGHT, + parent: containerId, + backgroundColor: '#0a1628', + scale: { + mode: Phaser.Scale.FIT, + autoCenter: Phaser.Scale.CENTER_BOTH, + }, + scene: [], // 在 GameScene 中动态加载 + physics: { + default: 'arcade', + arcade: { debug: false } + } + } +} +``` + +**`game/GameScene.ts`**(主游戏场景): + +核心地图渲染逻辑: +- 创建 16x12 格子网格 +- 根据 PATH_WAYPOINTS 计算路径格子集合(路径上的每个格子都是路径格) +- 路径格子:深褐色(`0x3d2b1f`) +- 可建塔格子:深蓝色(`0x1e3a5f`) +- 格子间有1px间距 +- 鼠标悬停非路径格子时高亮 +- 点击非路径格子:触发建塔逻辑(Phase 1先只做UI提示) +- 添加地图装饰性文字("面试间"、"财务室") +- 场景中显示:顶部 HUD 区域(KPI进度条 + HC数值) + +地图路径计算方式(不能只用 4 个坐标点,需要填充中间格子): +```typescript +// 将折线坐标展开为完整路径格子集合 +function buildPathTiles(waypoints): Set { + const tiles = new Set() + for (let i = 0; i < waypoints.length - 1; i++) { + const from = waypoints[i] + const to = waypoints[i + 1] + // 水平或垂直连线 + if (from.x === to.x) { + for (let y = Math.min(from.y, to.y); y <= Math.max(from.y, to.y); y++) { + tiles.add(`${from.x},${y}`) + } + } else { + for (let x = Math.min(from.x, to.x); x <= Math.max(from.x, to.x); x++) { + tiles.add(`${x},${from.y}`) + } + } + } + return tiles +} +``` + +HUD 设计(Phaser Text/Graphics 实现,而非 HTML 层): +- 顶部黑色半透明条(全宽,高度60px) +- 左:游戏标题 "大厂保卫战" (Press Start 2P,小字号) +- 中:KPI 进度条(宽300px,绿→黄→红渐变) +- 右:HC 数值 "HC: 200" + +**`game/GameManager.ts`**(游戏状态管理器): +```typescript +export class GameManager { + private static instance: GameManager + public hc: number = INITIAL_HC + public kpi: number = INITIAL_KPI + public currentWave: number = 0 + public gameState: 'idle' | 'playing' | 'paused' | 'victory' | 'defeat' = 'idle' + + static getInstance(): GameManager { ... } + + spendHC(amount: number): boolean { ... } // 扣除HC,不足返回false + addHC(amount: number): void { ... } + reduceKPI(amount: number): void { ... } // 减少KPI,归零触发失败 + + // 事件系统(用于 Scene 与 UI 通信) + onHCChange: ((hc: number) => void)[] = [] + onKPIChange: ((kpi: number) => void)[] = [] + onGameOver: (() => void)[] = [] + onVictory: (() => void)[] = [] +} +``` + +### Step 5: 更新 `app/layout.tsx` +在 layout 中引入 Google Fonts(Press Start 2P + VT323): +```tsx +// 在 中添加 + + + +``` + +### Step 6: 更新 `app/globals.css` +添加游戏全局样式: +- CSS 变量(颜色 token) +- CRT 扫描线效果(供主页使用) +- 霓虹发光 keyframes 动画 +- 像素字体 class + +## 验收标准 +1. `pnpm dev` 启动成功,无编译错误 +2. 访问 `/`:显示游戏封面,有进入游戏按钮,霓虹效果正常 +3. 点击进入游戏跳转至 `/game` +4. 访问 `/game`:渲染 16x12 格子地图,S型路径可见(深褐色路径,深蓝色可建区域) +5. 鼠标悬停可建塔格子时高亮 +6. 顶部 HUD 显示 KPI 进度条和 HC 数值 + +## 相关上下文 +- 设计系统:`design-system/大厂保卫战/MASTER.md` +- PRD:`.docs/prd-tower-defense-game.md` +- 计划文档:`.docs/plans/2026-03-21-tower-defense-game.md` +- 开发规范:`@rules/dev-best-practices.md`(**必须先阅读**) + +完成开发后,按 `@rules/atomic-commit.md` 规范,将变更按逻辑模块分组提交(禁止 `git add .`),确保所有文件均已 commit 后再结束任务。 diff --git a/.docs/tasks/task-1cd-towers-enemies.md b/.docs/tasks/task-1cd-towers-enemies.md new file mode 100644 index 0000000..bf59e5b --- /dev/null +++ b/.docs/tasks/task-1cd-towers-enemies.md @@ -0,0 +1,427 @@ +# Task 1C+1D: 防御塔系统 + 怪物波次系统 + +## 任务概要 +在已完成的 Phaser.js 游戏框架基础上,实现完整的防御塔系统(4种塔+部署逻辑+精力管理)和怪物波次系统(4种怪物+路径寻路+战斗逻辑+KPI扣减)。 + +**前置条件**:Task 1A+1B 已完成,`game/` 目录已存在,`game/GameScene.ts`、`game/constants.ts`、`game/GameManager.ts` 等已实现。 + +## 技术架构 + +``` +game/ +├── constants.ts ← 已存在 +├── config.ts ← 已存在 +├── GameScene.ts ← 已存在,需要集成新系统 +├── GameManager.ts ← 已存在 +├── towers/ +│ ├── TowerBase.ts ← 新建 +│ ├── InternTower.ts ← 新建(00后实习生) +│ ├── SeniorDevTower.ts ← 新建(P6资深开发) +│ ├── PPTMasterTower.ts ← 新建(PPT大师) +│ ├── HRBPTower.ts ← 新建(HRBP辅助塔) +│ └── TowerManager.ts ← 新建 +├── enemies/ +│ ├── EnemyBase.ts ← 新建 +│ ├── FreshGraduate.ts ← 新建(校招应届生) +│ ├── OldEmployee.ts ← 新建(35岁老员工) +│ ├── TroubleMaker.ts ← 新建(职业碰瓷王) +│ ├── BossVP.ts ← 新建(空降VP) +│ ├── EnemyPool.ts ← 新建(对象池) +│ └── WaveManager.ts ← 新建(波次管理器) +└── ui/ + ├── TowerPanel.ts ← 新建(底部塔选择面板) + └── HUD.ts ← 新建(升级 GameScene 中的 HUD 逻辑) +``` + +## 实现规范 + +### 防御塔系统 + +**`game/towers/TowerBase.ts`**: +```typescript +export abstract class TowerBase { + protected scene: Phaser.Scene + public gridX: number + public gridY: number + protected sprite: Phaser.GameObjects.Graphics // 用 Graphics 绘制(无美术资源) + protected staminaBar: Phaser.GameObjects.Graphics + + // 数值属性 + public readonly cost: number + public readonly attackRange: number // 格子单位 + public readonly attackDamage: number + public readonly attackSpeed: number // 每秒攻击次数 + public readonly maxStamina: number = 100 + public stamina: number = 100 + + protected attackCooldown: number = 0 + protected staminaRegen: number = 5 // 每秒恢复量(不攻击时) + protected isActive: boolean = true // false = 摸鱼状态 + + constructor(scene, gridX, gridY) { ... } + + // 每帧更新 + update(delta: number, enemies: EnemyBase[]): void { + this.attackCooldown -= delta + + if (this.stamina <= 0) { + this.isActive = false + } + + if (!this.isActive) { + // 摸鱼:恢复精力 + this.stamina = Math.min(this.maxStamina, this.stamina + this.staminaRegen * delta / 1000) + if (this.stamina > 0) this.isActive = true + this.updateStaminaBar() + return + } + + // 找目标并攻击 + const target = this.findTarget(enemies) + if (target && this.attackCooldown <= 0) { + this.attack(target) + this.stamina -= 5 // 攻击消耗精力 + this.attackCooldown = 1000 / this.attackSpeed + this.updateStaminaBar() + } else if (!target) { + // 没有目标时恢复精力 + this.stamina = Math.min(this.maxStamina, this.stamina + this.staminaRegen * delta / 1000) + this.updateStaminaBar() + } + } + + abstract attack(target: EnemyBase): void + abstract drawSprite(): void // 用 Graphics 绘制塔的外观 + + protected findTarget(enemies: EnemyBase[]): EnemyBase | null { + // 找射程内血量最多的怪(或最近怪) + ... + } + + protected updateStaminaBar(): void { + // 更新精力条颜色和宽度 + ... + } + + // 玩家点击塔时调用:花费10HC购买咖啡 + buyCoffee(): boolean { + const manager = GameManager.getInstance() + if (manager.spendHC(COFFEE_COST)) { + this.stamina = this.maxStamina + this.isActive = true + this.updateStaminaBar() + return true + } + return false + } + + destroy(): void { ... } +} +``` + +**4 种塔的具体实现:** + +`InternTower`(00后实习生,50HC): +- 视觉:绿色小人形状(Graphics 画) +- 攻击:近战(射程1格),单体,15伤害,1.5/s +- 特殊技能"整顿职场":每次攻击5%概率秒杀(血量<500的)普通怪物 +- 被动:每秒1%概率直接离场(调用 destroy,退还 25HC) +- 攻击方式:直接扣血(近战,不需要弹幕) + +`SeniorDevTower`(P6资深开发,120HC): +- 视觉:深蓝色方形,顶部有 `` 符号 +- 攻击:远程直线,30伤害,1.0/s,射程5格 +- 特殊技能"代码屎山":子弹命中后附加 DOT(10伤害/秒,持续3秒) +- 攻击方式:发射子弹(Graphics 绘制的绿色代码块),直线飞行 + +`PPTMasterTower`(PPT大师,100HC): +- 视觉:橙色圆形,有 PPT 图标 +- 攻击:范围AOE,5伤害,1.5/s,射程3格(圆形区域) +- 特殊技能"黑话领域":攻击命中的怪物移速降低40%,持续2秒 +- 攻击方式:爆炸效果(Graphics 绘制的扩散圆圈),范围内所有怪物受伤 + +`HRBPTower`(HRBP,80HC): +- 视觉:粉色,心形图案 +- 攻击:无直接伤害(辅助塔) +- 特殊技能"打鸡血":每0.5秒给周围1格内的塔攻速+20%(叠加BUFF),每次BUFF消耗自身5精力 +- 辅助方式:周期性扫描周围格子,找到塔就给BUFF + +**`game/towers/TowerManager.ts`**: +- 维护所有已部署塔的数组 +- 处理建塔请求:检查格子合法性(非路径+未占用),扣除HC +- 每帧调用所有塔的 update(delta, enemies) +- 处理塔的点击事件(显示购买咖啡的提示) + +**`game/ui/TowerPanel.ts`**(底部塔选择面板,在 Phaser Scene 内用 DOM 或 Phaser.GameObjects 实现): +- 优先使用 HTML DOM 覆盖层(`document.createElement`),绝对定位在 Canvas 底部 +- 4个塔卡片,显示:像素风格图标 + 名称 + 成本 +- 当前选中的塔有高亮边框 +- HC 不足时显示灰色不可点击状态 +- 点击塔卡片后,鼠标进入"建塔模式",下一次点击地图格子时放置塔 + +--- + +### 怪物波次系统 + +**`game/enemies/EnemyBase.ts`**: +```typescript +export abstract class EnemyBase { + protected scene: Phaser.Scene + protected sprite: Phaser.GameObjects.Graphics + protected healthBar: Phaser.GameObjects.Graphics + protected quoteText: Phaser.GameObjects.Text // 头顶语录 + + public maxHp: number + public hp: number + public speed: number // 像素/秒 + public readonly kpiDamage: number // 到达终点扣除的 KPI + public readonly hcReward: number // 死亡奖励的 HC + + // 路径相关 + protected pathPoints: { x: number, y: number }[] // 像素坐标路径 + protected currentPathIndex: number = 0 + protected distanceTraveled: number = 0 + + // 状态 + public isDead: boolean = false + public isActive: boolean = true + public dotEffects: { damage: number, duration: number, timer: number }[] = [] // DOT效果 + public slowEffect: number = 0 // 减速百分比(0~1) + public slowTimer: number = 0 + public shieldCount: number = 0 // 护盾层数(老员工专用) + + constructor(scene, pathPoints) { ... } + + update(delta: number): void { + if (this.isDead || !this.isActive) return + + // 处理DOT效果 + this.processDOT(delta) + + // 处理减速 + if (this.slowTimer > 0) { + this.slowTimer -= delta + if (this.slowTimer <= 0) this.slowEffect = 0 + } + + // 沿路径移动 + this.moveAlongPath(delta) + + // 更新血条位置 + this.updateHealthBar() + this.updateQuoteText() + } + + protected moveAlongPath(delta: number): void { + if (this.currentPathIndex >= this.pathPoints.length - 1) { + // 到达终点 + this.reachEnd() + return + } + + const target = this.pathPoints[this.currentPathIndex + 1] + const currentSpeed = this.speed * (1 - this.slowEffect) + const distance = currentSpeed * delta / 1000 + + // 向目标移动 + ...向 target 方向移动 distance 像素... + + // 到达当前路径点后切换到下一个 + ... + } + + takeDamage(damage: number): void { + if (this.shieldCount > 0) { + this.shieldCount-- + return // 护盾吸收 + } + this.hp -= damage + this.updateHealthBar() + if (this.hp <= 0) { + this.die() + } + } + + addDOT(damage: number, duration: number): void { + this.dotEffects.push({ damage, duration, timer: duration }) + } + + addSlow(percent: number, duration: number): void { + this.slowEffect = Math.max(this.slowEffect, percent) + this.slowTimer = Math.max(this.slowTimer, duration) + } + + protected die(): void { + this.isDead = true + const manager = GameManager.getInstance() + manager.addHC(this.hcReward) + this.onDeath() // 子类可重写(碰瓷王扣HC) + this.destroy() + } + + protected reachEnd(): void { + const manager = GameManager.getInstance() + manager.reduceKPI(this.kpiDamage) + this.isDead = true + this.destroy() + } + + protected onDeath(): void {} // 子类重写 + + abstract drawSprite(): void + abstract getQuote(): string // 返回该怪物的语录 + + destroy(): void { + this.sprite?.destroy() + this.healthBar?.destroy() + this.quoteText?.destroy() + } +} +``` + +**4 种怪物:** + +`FreshGraduate`(校招应届生): +- HP: 30, 速度: 120px/s, KPI扣减: 2%, HC奖励: 10 +- 护盾: 0 +- 视觉:小绿点(Graphics 小圆) +- 语录随机:["求转正!", "我愿意加班!", "卷!卷!卷!"] + +`OldEmployee`(35岁老员工): +- HP: 150, 速度: 50px/s, KPI扣减: 8%, HC奖励: 30 +- 护盾: 3层(shieldCount=3,前3次攻击无效) +- 视觉:大蓝方块 +- 语录:["我为公司立过功!", "我有10年经验!", "年龄不是问题!"] + +`TroubleMaker`(职业碰瓷王): +- HP: 80, 速度: 80px/s, KPI扣减: 5%, HC奖励: 20 +- 特殊:死亡时重写 `onDeath()` 扣玩家20 HC(劳动仲裁) +- 视觉:红色叹号形状 +- 语录:["录音笔已开启", "这是违法的!", "我要仲裁!"] + +`BossVP`(空降VP): +- HP: 800, 速度: 40px/s, KPI扣减: 30%, HC奖励: 150 +- 技能"组织架构调整":每20秒触发一次,随机摧毁场上一个防御塔 +- 视觉:大金色六边形,周围有特效 +- 语录:["我来教大家怎么做事", "你们缺乏战略眼光", "这不是执行力的问题"] +- BOSS出现时:全屏红色闪光效果(Phaser Camera flash) + +**`game/enemies/EnemyPool.ts`**(对象池): +简单实现:维护一个 inactive 数组,acquire 时取出复用,release 时放回。 + +**`game/enemies/WaveManager.ts`**(波次管理器): +```typescript +// 6波配置 +const WAVE_CONFIG = [ + { enemies: [{ type: 'FreshGraduate', count: 10, interval: 800 }] }, // 第1波 + { enemies: [{ type: 'FreshGraduate', count: 8, interval: 800 }, { type: 'OldEmployee', count: 3, interval: 2000 }] }, + { enemies: [{ type: 'OldEmployee', count: 5, interval: 2000 }, { type: 'TroubleMaker', count: 3, interval: 1500 }] }, // 周报前 + { enemies: [{ type: 'FreshGraduate', count: 12, interval: 600 }, { type: 'TroubleMaker', count: 2, interval: 1500 }] }, + { enemies: [{ type: 'OldEmployee', count: 6, interval: 1500 }, { type: 'TroubleMaker', count: 3, interval: 1500 }] }, + { enemies: [{ type: 'BossVP', count: 1, interval: 0 }, { type: 'OldEmployee', count: 4, interval: 2000 }] }, // Boss波 +] + +export class WaveManager { + private currentWave: number = 0 + private isSpawning: boolean = false + private waveComplete: boolean = false + + // 周报触发:第3波结束后 + private onWave3Complete: () => void + // 胜利:第6波全部消灭 + private onAllWavesComplete: () => void + + startNextWave(): void { ... } + private spawnEnemy(type: string): EnemyBase { ... } + update(delta: number): void { ... } // 每帧更新所有活跃怪物 + + getAllActiveEnemies(): EnemyBase[] { ... } +} +``` + +**路径像素坐标计算**: +PATH_WAYPOINTS 是格子坐标,需要转换为 Phaser Canvas 像素坐标: +```typescript +// 格子坐标转像素坐标(格子中心) +function gridToPixel(gridX, gridY): { x, y } { + return { + x: gridX * TILE_SIZE + TILE_SIZE / 2, + y: gridY * TILE_SIZE + TILE_SIZE / 2 + HUD_HEIGHT // 加上顶部HUD高度 + } +} +``` + +完整路径像素坐标通过对 PATH_WAYPOINTS 相邻点之间插值生成: +```typescript +// 将 PATH_WAYPOINTS 扩展为完整的转折路径(每个转折点都在路径中) +function buildFullPath(): { x, y }[] { + const points = [] + for (let i = 0; i < PATH_WAYPOINTS.length - 1; i++) { + const from = gridToPixel(PATH_WAYPOINTS[i].x, PATH_WAYPOINTS[i].y) + const to = gridToPixel(PATH_WAYPOINTS[i+1].x, PATH_WAYPOINTS[i+1].y) + points.push(from) + points.push(to) + } + // 去重相邻相同点 + return points.filter((p, i, arr) => + i === 0 || p.x !== arr[i-1].x || p.y !== arr[i-1].y + ) +} +``` + +--- + +### HUD 与战斗整合 + +更新 `game/GameScene.ts`,集成两个新系统: +1. 在场景 `create()` 中初始化 TowerManager 和 WaveManager +2. 在场景 `update(time, delta)` 中: + - 调用 `towerManager.update(delta, waveManager.getAllActiveEnemies())` + - 调用 `waveManager.update(delta)` + - 更新 HUD(KPI条、HC显示) +3. 处理格子点击事件:若处于"建塔模式",调用 `towerManager.placeTower(gridX, gridY, selectedTowerType)` +4. 添加"开始下一波"按钮(按钮文字"召唤下一波") +5. 波次开始时显示波次提示文字("第X波来袭!") + +--- + +### 视觉规范(Graphics 绘制指南,无美术资源) + +所有游戏对象用 Phaser.GameObjects.Graphics 绘制: + +| 对象 | 形状 | 颜色 | +|------|------|------| +| 00后实习生 | 小圆(r=12) + 十字形身体 | `#22C55E`(绿) | +| P6资深开发 | 方块(24x24) + `` 文字 | `#3B82F6`(蓝) | +| PPT大师 | 圆(r=16) + 圆心白点 | `#F59E0B`(橙) | +| HRBP | 菱形(16x16) | `#EC4899`(粉) | +| 校招应届生 | 小圆(r=8) | `#86EFAC`(浅绿) | +| 35岁老员工 | 方块(20x20) + 护盾外框 | `#93C5FD`(浅蓝) | +| 职业碰瓷王 | 三角形 | `#FCA5A5`(浅红) | +| 空降VP | 六边形(r=30) + 金色外框 | `#FBBF24`(金色) | + +塔的精力条:宽 40px,高 4px,黄色 `#F59E0B`,底部深灰 `#374151` +怪物血条:宽 30px,高 4px,绿→红渐变(按血量百分比) + +--- + +## 验收标准 + +1. 可以在地图格子上部署 4 种塔,扣除正确 HC +2. 点击已部署的塔显示精力信息,可以花 10HC 购买咖啡 +3. 点击"召唤下一波"按钮,怪物从起点出现并沿 S 型路径移动到终点 +4. 塔会自动攻击射程内的怪物 +5. 怪物被消灭后奖励 HC,到达终点后扣除 KPI +6. KPI 条和 HC 显示实时更新 +7. 35岁老员工前 3 次攻击不掉血(护盾) +8. 职业碰瓷王死亡时扣除玩家 20 HC +9. 完整 6 波可以正常触发 + +## 相关上下文 +- 设计系统:`design-system/大厂保卫战/MASTER.md` +- PRD:`.docs/prd-tower-defense-game.md` +- 计划文档:`.docs/plans/2026-03-21-tower-defense-game.md` +- Task 1A+1B 的产出(已在 `game/` 目录中) +- 开发规范:`@rules/dev-best-practices.md`(**必须先阅读**) + +完成开发后,按 `@rules/atomic-commit.md` 规范,将变更按逻辑模块分组提交(禁止 `git add .`),确保所有文件均已 commit 后再结束任务。 diff --git a/.docs/tasks/task-2ab-gameplay-completion.md b/.docs/tasks/task-2ab-gameplay-completion.md new file mode 100644 index 0000000..bc9237e --- /dev/null +++ b/.docs/tasks/task-2ab-gameplay-completion.md @@ -0,0 +1,223 @@ +# Task 2A+2B: 周报结算系统 + 特殊技能完善 + 胜利失败界面 + 怪物语录 + +## 任务概要 +在已完成的游戏基础框架上,实现周报结算弹窗(每3波触发)、完善各塔特殊技能、实现胜利/失败结算界面、以及怪物头顶语录飘字系统。 + +**前置条件**:Task 1A+1B+1C+1D 已完成,完整的游戏框架已运行。 + +## 需要了解的已有文件 + +先读取以下文件了解现有实现: +- `game/GameManager.ts`(游戏状态,了解事件系统接口) +- `game/GameScene.ts`(主场景,了解如何集成新UI) +- `game/ui/HUD.ts`(已有HUD,了解胜利/失败通知已有实现) +- `game/enemies/WaveManager.ts`(了解波次完成事件是如何触发的) + +## 实现内容 + +### 1. 周报结算弹窗(`game/ui/WeeklyReportModal.ts`) + +**触发时机**:第3波结束后(WaveManager 已有 `onWeeklyReport` 回调) + +**弹窗设计**(全屏覆盖 DOM 层,仿钉钉/飞书全屏通知): +``` +[全屏黑色半透明背景(z-index: 9999)] +[中央弹窗卡片,深色背景 #1a1a2e,霓虹紫边框] + 标题:"📊 周报提交时间!"(用 img/SVG 图标代替 emoji,或纯文字) + 副标题:"请选择正确的互联网黑话,提交本周工作总结" + [10秒倒计时进度条] — 顶部红色进度条,从满到空 + + 3个选项卡片(随机从黑话题库选3个,其中1个是"正确答案"): + [选项A] [选项B] [选项C] + + 答题结果区域(答题后显示): + - 选对:绿色提示 "正确!获得奖励:+80 HC" 或 "全场精力已补满" + - 选错/超时:红色提示 "答错了!老板视察3秒!全场塔禁锢!" +``` + +**黑话题库**(在 `game/data/buzzwords.ts` 中定义100条): +```typescript +// 分为两类:正确答案类(互联网常见黑话)+ 干扰项 +export const BUZZWORDS = [ + // 正确答案(真实大厂黑话) + "赋能", "对齐", "颗粒度", "打通", "闭环", "抓手", "生态", "赛道", + "护城河", "降维打击", "底层逻辑", "顶层设计", "方法论", "复盘", + "迭代", "串联", "拉齐", "落地", "沉淀", "跑通", "MVP", "ROI", + "私域流量", "增量市场", "存量竞争", "心智占位", "势能积累", + "组织赋能", "生态打通", "战略对齐", "价值共创", "协同增效", + "降本增效", "提质增效", "精益管理", "敏捷开发", "极致体验", + "用户心智", "品牌溢价", "流量变现", "私域运营", "全渠道", + "中台建设", "数字化转型", "智能升级", "生态圈", "护城河", + "弯道超车", "换道超车", "破圈", "出圈", "刷屏", "爆款", + "现象级", "标杆案例", "对标", "copy from", "跑模型", + // 干扰项(故意写错/不通顺的) + "增熵赋能", "横向闭环", "负向对齐", "反底层逻辑", +] + +// 每次从 BUZZWORDS 随机取3个,其中第一个是"正确答案" +// 随机打乱顺序后展示 +export function generateWeeklyOptions(): { options: string[], correctIndex: number } { + const shuffled = [...BUZZWORDS].sort(() => Math.random() - 0.5) + const correct = shuffled[0] + const options = [correct, shuffled[1], shuffled[2]].sort(() => Math.random() - 0.5) + return { + options, + correctIndex: options.indexOf(correct) + } +} +``` + +**倒计时逻辑**: +- 10秒倒计时,使用 `setInterval` +- 超时自动触发"选错"结果 +- 选择后立即停止倒计时,显示结果 2 秒后自动关闭弹窗 + +**结果逻辑**: +```typescript +// 选对:随机50%概率给HC,50%概率补满精力 +if (Math.random() > 0.5) { + gameManager.addHC(50 + Math.floor(Math.random() * 50)) + showResult('success', `获得 ${reward} HC!继续加油!`) +} else { + // 通知所有塔补满精力(通过 GameScene 的回调) + onFullStamina() + showResult('success', '全场精力已补满!') +} + +// 选错/超时:触发全场塔禁锢3秒 +onBossInspection() // 调用 GameScene 的回调,禁锢所有塔 +showResult('error', '老板来视察了!全场禁锢3秒!') +``` + +### 2. 完善胜利/失败界面(更新 `game/ui/HUD.ts` 或单独文件) + +当前 HUD 可能已有简单的胜利/失败提示,需要升级为完整的模态弹窗。 + +**失败弹窗**(KPI 归零时触发,仿钉钉退群通知): +``` +[弹窗容器,白色背景,仿钉钉卡片样式] + [顶部蓝色条] 系统通知 + [钉钉风格图标] + + 正文: + 鉴于您近期的表现未能达成业务闭环, + 经公司管理层研究决定, + 对您进行"毕业"处理。 + 请于5分钟内归还工牌,不要带走办公文具。 + + [领取 N+1] (重新开始,刷新页面) + [申请仲裁] (返回主页,跳转 /) +``` + +**胜利弹窗**(全6波清空后,仿绩效评级系统): +``` +[弹窗容器,深色背景,金色边框] + 标题:绩效评级公示 + + 最终KPI: XX% + 消灭怪物: XX只 + 剩余HC: XX + + 绩效等级: + - KPI >= 80%: S级 "超出预期" + - KPI >= 60%: A级 "达成预期" + - KPI >= 40%: B级 "基本达成" + - KPI < 40%: C级 "未达预期" + + 等级对应奖励描述(黑色幽默): + - S级:"恭喜晋升为高级打工人!福报已在路上..." + - A级:"表现良好,明年涨薪3%(扣去通胀后-2%)" + - B级:"还需努力,下月开始996" + - C级:"已被纳入待优化名单" + + [再战一局] (重新开始) + [见好就收] (返回主页) +``` + +### 3. 怪物头顶语录飘字(更新 `game/enemies/EnemyBase.ts`) + +EnemyBase 中的 `quoteText` 字段已预留。现在实现完整的语录系统: + +**`game/data/quotes.ts`**(100条语录分怪物类型): +```typescript +export const QUOTES = { + FreshGraduate: [ + "求转正!", "我愿意加班!", "卷!卷!卷!", + "内推有名额吗?", "实习补贴够买咖啡吗?", + "我会CRUD!", "熟练掌握Word和Excel", + "我有五年实习经验!", "大厂梦...", "期待来公司学习!", + "我不需要工资,只需要经验!", "比同龄人卷!", + ], + OldEmployee: [ + "我为公司立过功!", "我有10年经验!", + "年龄不是问题!", "那时候还是我搭的架构", + "当年我一个人顶三个人!", "这个需求做不了", + "怎么可能这么快做完!", "文档?从来没有!", + "测试?QA负责的!", "这是历史遗留问题", + ], + TroubleMaker: [ + "录音笔已开启", "这是违法的!", + "我要仲裁!", "劳动法第X条规定...", + "我已咨询过律师", "保留证据中...", + "你们的违规操作我都记录了", "看我不告你们!", + ], + BossVP: [ + "我来教大家怎么做事", "你们缺乏战略眼光", + "这不是执行力的问题", "格局太小了", + "你们都不懂商业本质", "要有全局思维", + "小事不过手,大事全拍板", "你们的方案需要颠覆性重构", + "我在BAT做过这个", "先对齐一下认知", + ], +} +``` + +**语录飘字动效**: +- 怪物出生后 0.5 秒显示语录(随机从该类型的语录库中取) +- 字体:VT323,白色 +- 文字在怪物头顶30px处显示 +- 3秒后淡出(alpha 从1到0的 Tween 动画) +- 文字跟随怪物移动 + +### 4. 全场塔禁锢效果(`GameScene` 中添加) + +当周报答错/超时时,触发"老板视察": +```typescript +// 在 GameScene 中添加 +freezeAllTowers(duration: number = 3000): void { + this.towerManager.getAllTowers().forEach(tower => { + tower.isFrozen = true + // 视觉:塔变灰色 + setTimeout(() => { + tower.isFrozen = false + // 视觉:恢复正常颜色 + }, duration) + }) +} + +refillAllStamina(): void { + this.towerManager.getAllTowers().forEach(tower => { + tower.stamina = tower.maxStamina + tower.isActive = true + }) +} +``` + +在 TowerBase 的 `update()` 中检查 `isFrozen` 状态,如果为 true 则跳过攻击。 + +## 验收标准 + +1. 第3波全部怪物消灭后,弹出周报弹窗,有10秒倒计时 +2. 选对答案:随机给予HC奖励或补满精力 +3. 选错/超时:全场塔3秒无法攻击(显示灰色) +4. KPI归零时:弹出仿钉钉失败弹窗,两个按钮功能正确 +5. 第6波全部消灭后:弹出绩效评级胜利弹窗,根据最终KPI显示正确等级 +6. 所有怪物头顶有飘字语录,随怪物移动,3秒后淡出 + +## 相关上下文 +- 设计系统:`design-system/大厂保卫战/MASTER.md` +- PRD:`.docs/prd-tower-defense-game.md` +- 计划文档:`.docs/plans/2026-03-21-tower-defense-game.md` +- 开发规范:`@rules/dev-best-practices.md`(**必须先阅读**) +- 原子提交规范:`@rules/atomic-commit.md` + +完成开发后,按 `@rules/atomic-commit.md` 规范,将变更按逻辑模块分组提交(禁止 `git add .`),确保所有文件均已 commit 后再结束任务。 diff --git a/design-system/大厂保卫战/MASTER.md b/design-system/大厂保卫战/MASTER.md new file mode 100644 index 0000000..7110ee3 --- /dev/null +++ b/design-system/大厂保卫战/MASTER.md @@ -0,0 +1,288 @@ +# Design System Master File + +> **LOGIC:** When building a specific page, first check `design-system/pages/[page-name].md`. +> If that file exists, its rules **override** this Master file. +> If not, strictly follow the rules below. + +--- + +**Project:** 大厂保卫战 +**Generated:** 2026-03-21 07:46:37 +**Category:** Gaming + +--- + +## Global Rules + +### Color Palette + +| Role | Hex | CSS Variable | +|------|-----|--------------| +| Primary | `#7C3AED` | `--color-primary` | +| Secondary | `#A78BFA` | `--color-secondary` | +| CTA/Accent | `#F43F5E` | `--color-cta` | +| Background | `#0F0F23` | `--color-background` | +| Text | `#E2E8F0` | `--color-text` | + +**Color Notes:** Neon purple + rose action + +### Typography + +- **Heading Font:** Press Start 2P +- **Body Font:** VT323 +- **Mood:** pixel, retro, gaming, 8-bit, nostalgic, arcade +- **Google Fonts:** [Press Start 2P + VT323](https://fonts.google.com/share?selection.family=Press+Start+2P|VT323) + +**CSS Import:** +```css +@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap'); +``` + +### Spacing Variables + +| Token | Value | Usage | +|-------|-------|-------| +| `--space-xs` | `4px` / `0.25rem` | Tight gaps | +| `--space-sm` | `8px` / `0.5rem` | Icon gaps, inline spacing | +| `--space-md` | `16px` / `1rem` | Standard padding | +| `--space-lg` | `24px` / `1.5rem` | Section padding | +| `--space-xl` | `32px` / `2rem` | Large gaps | +| `--space-2xl` | `48px` / `3rem` | Section margins | +| `--space-3xl` | `64px` / `4rem` | Hero padding | + +### Shadow Depths + +| Level | Value | Usage | +|-------|-------|-------| +| `--shadow-sm` | `0 1px 2px rgba(0,0,0,0.05)` | Subtle lift | +| `--shadow-md` | `0 4px 6px rgba(0,0,0,0.1)` | Cards, buttons | +| `--shadow-lg` | `0 10px 15px rgba(0,0,0,0.1)` | Modals, dropdowns | +| `--shadow-xl` | `0 20px 25px rgba(0,0,0,0.15)` | Hero images, featured cards | + +--- + +## Component Specs + +### Buttons + +```css +/* Primary Button */ +.btn-primary { + background: #F43F5E; + color: white; + padding: 12px 24px; + border-radius: 8px; + font-weight: 600; + transition: all 200ms ease; + cursor: pointer; +} + +.btn-primary:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +/* Secondary Button */ +.btn-secondary { + background: transparent; + color: #7C3AED; + border: 2px solid #7C3AED; + padding: 12px 24px; + border-radius: 8px; + font-weight: 600; + transition: all 200ms ease; + cursor: pointer; +} +``` + +### Cards + +```css +.card { + background: #0F0F23; + border-radius: 12px; + padding: 24px; + box-shadow: var(--shadow-md); + transition: all 200ms ease; + cursor: pointer; +} + +.card:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-2px); +} +``` + +### Inputs + +```css +.input { + padding: 12px 16px; + border: 1px solid #E2E8F0; + border-radius: 8px; + font-size: 16px; + transition: border-color 200ms ease; +} + +.input:focus { + border-color: #7C3AED; + outline: none; + box-shadow: 0 0 0 3px #7C3AED20; +} +``` + +### Modals + +```css +.modal-overlay { + background: rgba(0, 0, 0, 0.5); + backdrop-filter: blur(4px); +} + +.modal { + background: white; + border-radius: 16px; + padding: 32px; + box-shadow: var(--shadow-xl); + max-width: 500px; + width: 90%; +} +``` + +--- + +## Style Guidelines + +**Style:** Retro-Futurism + +**Keywords:** Vintage sci-fi, 80s aesthetic, neon glow, geometric patterns, CRT scanlines, pixel art, cyberpunk, synthwave + +**Best For:** Gaming, entertainment, music platforms, tech brands, artistic projects, nostalgic, cyberpunk + +**Key Effects:** CRT scanlines (::before overlay), neon glow (text-shadow+box-shadow), glitch effects (skew/offset keyframes) + +### Page Pattern + +**Pattern Name:** Horizontal Scroll Journey + +- **Conversion Strategy:** Immersive product discovery. High engagement. Keep navigation visible. +28,Bento Grid Showcase,bento, grid, features, modular, apple-style, showcase", 1. Hero, 2. Bento Grid (Key Features), 3. Detail Cards, 4. Tech Specs, 5. CTA, Floating Action Button or Bottom of Grid, Card backgrounds: #F5F5F7 or Glass. Icons: Vibrant brand colors. Text: Dark., Hover card scale (1.02), video inside cards, tilt effect, staggered reveal, Scannable value props. High information density without clutter. Mobile stack. +29,Interactive 3D Configurator,3d, configurator, customizer, interactive, product", 1. Hero (Configurator), 2. Feature Highlight (synced), 3. Price/Specs, 4. Purchase, Inside Configurator UI + Sticky Bottom Bar, Neutral studio background. Product: Realistic materials. UI: Minimal overlay., Real-time rendering, material swap animation, camera rotate/zoom, light reflection, Increases ownership feeling. 360 view reduces return rates. Direct add-to-cart. +30,AI-Driven Dynamic Landing,ai, dynamic, personalized, adaptive, generative", 1. Prompt/Input Hero, 2. Generated Result Preview, 3. How it Works, 4. Value Prop, Input Field (Hero) + 'Try it' Buttons, Adaptive to user input. Dark mode for compute feel. Neon accents., Typing text effects, shimmering generation loaders, morphing layouts, Immediate value demonstration. 'Show, don't tell'. Low friction start. +- **CTA Placement:** Floating Sticky CTA or End of Horizontal Track +- **Section Order:** 1. Intro (Vertical), 2. The Journey (Horizontal Track), 3. Detail Reveal, 4. Vertical Footer + +--- + +## Anti-Patterns (Do NOT Use) + +- ❌ Minimalist design +- ❌ Static assets + +### Additional Forbidden Patterns + +- ❌ **Emojis as icons** — Use SVG icons (Heroicons, Lucide, Simple Icons) +- ❌ **Missing cursor:pointer** — All clickable elements must have cursor:pointer +- ❌ **Layout-shifting hovers** — Avoid scale transforms that shift layout +- ❌ **Low contrast text** — Maintain 4.5:1 minimum contrast ratio +- ❌ **Instant state changes** — Always use transitions (150-300ms) +- ❌ **Invisible focus states** — Focus states must be visible for a11y + +--- + +## 产品定位与目标用户分析 + +**产品定位:** 面向互联网从业者的2D讽刺类塔防小游戏,以"大厂职场文化"为世界观,用黑色幽默和荒诞策略玩法提供情绪出口。 + +**目标用户:** 25-35岁互联网从业者,对内卷/福报/绩效考核有亲身体验,游戏轻度玩家。 + +**差异化:** 游戏内所有数值、界面、怪物都是职场文化的隐喻,产生强烈共情与自嘲效果。 + +--- + +## 布局模式与结构(Layout Strategy) + +**选定模式:** 模式 B — 内容展示型(游戏全屏内容为主) + +**页面结构:** +- **主页 `/`**:全屏游戏入口,顶部 Navbar(游戏名+状态栏),中央游戏 Canvas,底部版权 +- **游戏 Canvas 区域**:1280x720 基准,等比缩放适配不同分辨率 +- **HUD(游戏内界面)**:叠加在 Canvas 上的 HTML 层(KPI 条、HC 显示、塔选择面板) + +**页面区域划分:** +``` +┌────────────────────────────────────────────────┐ +│ [游戏名称] [KPI: 87%] [HC: 320] │ ← Navbar/HUD +├────────────────────────────────────────────────┤ +│ │ +│ 游戏 Canvas │ ← Phaser.js Canvas (全屏) +│ (地图 + 防御塔 + 怪物 + 弹幕) │ +│ │ +├────────────────────────────────────────────────┤ +│ [00后实习生 50HC] [P6开发 120HC] [PPT大师 100HC] │ ← 底部塔选择栏 +└────────────────────────────────────────────────┘ +``` + +**移动端适配:** 优先PC(1024px+),移动端(768px以下)保持可玩性,使用触摸拖放部署塔 + +**Nova SDK 组件:** 本项目为纯游戏,不使用 Nova SDK 组件,改用 Phaser.js 引擎 + +--- + +## 游戏专属视觉设计 + +### 颜色覆盖(游戏内专属) +| 用途 | Hex | 说明 | +|------|-----|------| +| 地图格子(可建塔区) | `#1E3A5F` | 深蓝格子 | +| 地图格子(路径) | `#2D1B00` | 深褐走廊 | +| KPI 条(健康) | `#22C55E` | 绿色正常 | +| KPI 条(危险) | `#EF4444` | 红色告警 | +| 精力条 | `#F59E0B` | 橙黄活力 | +| HC 数值 | `#A78BFA` | 紫色资源 | +| BOSS 警告 | `#FF4E00` | 橙红警报 | + +### 游戏 UI 组件设计(HUD) +- **KPI 进度条**:像素风格,带扫描线效果,颜色渐变(绿→黄→红) +- **HC 计数器**:仿钉钉/飞书通知badge样式,数字跳动动画 +- **塔选择卡片**:飞书卡片质感,深色毛玻璃背景,霓虹边框 +- **周报弹窗**:仿钉钉全屏通知,强制交互设计 +- **语录飘字**:怪物头顶白色文字,淡入淡出,字体 VT323 +- **失败弹窗**:完全仿钉钉退群通知,灰白配色反差 + +### 关键页面交互设计 +1. **主游戏页面**: + - 点击地图格子 → 弹出塔选择浮层(若 HC 不足则提示) + - 点击已部署的塔 → 显示精力条 + 购买咖啡按钮 + - 波次开始前 3 秒倒计时提示 + - BOSS 出现时全屏红色闪光 + 警报音效 + +2. **周报结算弹窗**: + - 全屏遮罩(不可点击背景关闭) + - 10 秒倒计时进度条(顶部) + - 3 个黑话选项卡片,答错/超时变红闪烁 + +3. **胜利/失败结算**: + - 仿绩效系统界面(胜利) + - 仿钉钉退群通知(失败) + +--- + +## Pre-Delivery Checklist + +Before delivering any UI code, verify: + +- [ ] No emojis used as icons (use SVG instead) +- [ ] All icons from consistent icon set (Heroicons/Lucide) +- [ ] `cursor-pointer` on all clickable elements +- [ ] Hover states with smooth transitions (150-300ms) +- [ ] Light mode: text contrast 4.5:1 minimum +- [ ] Focus states visible for keyboard navigation +- [ ] `prefers-reduced-motion` respected +- [ ] Responsive: 375px, 768px, 1024px, 1440px +- [ ] No content hidden behind fixed navbars +- [ ] No horizontal scroll on mobile +- [ ] 游戏 Canvas 在 1280x720 基准下布局正确 +- [ ] 所有 HUD 元素覆盖在 Canvas 正确位置 +- [ ] 波次系统正常触发,6 波全部可完成 +- [ ] 周报结算系统在第 3 波后正确触发