diff --git a/app/api/pua-score/route.ts b/app/api/pua-score/route.ts new file mode 100644 index 0000000..957e066 --- /dev/null +++ b/app/api/pua-score/route.ts @@ -0,0 +1,71 @@ +import { NextResponse } from 'next/server' +import { llmClient } from '@/app/api/llm-client' + +const SYSTEM_PROMPT = `你是一个专门分析互联网大厂老板PUA话术的AI裁判。 +用户输入一段老板/领导对员工说的"打鸡血"或"PUA"的话,你需要分析这段话并给出评分和游戏效果。 + +评分标准(1-10分): +1-3分:废话/没营养("大家加油"之类)→ 轻微效果 +4-6分:标准大厂黑话("对齐""闭环""狼性")→ 中等效果 +7-8分:强力PUA("996是福报""不拼搏对不起父母")→ 强力效果 +9-10分:终极毒鸡汤(极限施压/情感绑架)→ 全场爆发 + +只返回如下JSON,不要其他内容: +{ + "score": 数字(1-10), + "title": "效果名称(2-6个汉字,有创意)", + "desc": "对话语的一句话点评(15字以内,带点讽刺)", + "effect": "attack_boost" | "speed_boost" | "money_rain" | "rage_mode" | "backfire" +} + +effect说明: +- attack_boost: 攻击力提升(score 4-6) +- speed_boost: 攻击速度提升(score 7-8) +- money_rain: HC暴增(score 5-7,话语中强调利益) +- rage_mode: 全场狂暴(score 9-10) +- backfire: 话太废了反而debuff(score 1-2)` + +export async function POST(req: Request) { + try { + const { text } = await req.json() + if (!text || typeof text !== 'string' || text.trim().length < 2) { + return NextResponse.json({ error: '输入内容太短了' }, { status: 400 }) + } + + const resp = await llmClient.chat({ + model: 'gpt-4o-mini', + messages: [ + { role: 'system', content: SYSTEM_PROMPT }, + { role: 'user', content: text.slice(0, 200) }, + ], + maxTokens: 150, + temperature: 0.7, + }) + + const raw = resp.choices?.[0]?.message?.content ?? '' + + // 提取 JSON(防止 LLM 多输出文字) + const match = raw.match(/\{[\s\S]*\}/) + if (!match) throw new Error('LLM 返回格式异常') + + const result = JSON.parse(match[0]) + const score = Math.max(1, Math.min(10, Number(result.score) || 5)) + + return NextResponse.json({ + score, + title: result.title || '打鸡血', + desc: result.desc || '还行吧', + effect: result.effect || 'attack_boost', + }) + } catch (e) { + console.error('[pua-score]', e) + // 降级:随机给个分数 + const score = Math.floor(Math.random() * 7) + 3 + return NextResponse.json({ + score, + title: '随机鸡血', + desc: 'AI开小差了,随机发力', + effect: 'attack_boost', + }) + } +} diff --git a/app/game/page.tsx b/app/game/page.tsx index 7bff5c2..941d6e5 100644 --- a/app/game/page.tsx +++ b/app/game/page.tsx @@ -3,17 +3,293 @@ import { useEffect, useRef, useState, useCallback } from 'react' const TOWER_META = [ - { type: 'outsource', name: '外包程序员', cost: 30, desc: '近战 8伤 0.7/s', color: '#94A3B8', img: '/game-assets/tower-outsource.png', tip: '廉价但30%丢空,5%自伤' }, - { type: 'intern', name: '00后实习生', cost: 50, desc: '近战 15伤 1.5/s', color: '#22C55E', img: '/game-assets/tower-intern.png', tip: '整顿职场:5%概率秒杀' }, - { type: 'hrbp', name: 'HRBP', cost: 80, desc: '辅助 +20%攻速', color: '#EC4899', img: '/game-assets/tower-hrbp.png', tip: '打鸡血:周围塔攻速+20%' }, - { type: 'ops', name: '运营专员', cost: 90, desc: '远程 18伤 范围溅射', color: '#8B5CF6', img: '/game-assets/tower-ops.png', tip: '增长黑客:20%双倍HC' }, - { type: 'ppt', name: 'PPT大师', cost: 100, desc: 'AOE减速 5伤', color: '#F59E0B', img: '/game-assets/tower-ppt.png', tip: '黑话领域:减速40%' }, - { type: 'senior', name: 'P6资深开发', cost: 120, desc: '远程 30伤 5格射程', color: '#3B82F6', img: '/game-assets/tower-senior.png', tip: '代码屎山:附带持续伤害' }, - { type: 'pm', name: '产品经理', cost: 160, desc: '远程 20伤 需求变更', color: '#06B6D4', img: '/game-assets/tower-pm.png', tip: '需求变更:每4次把怪打回去' }, + { type: 'outsource', name: '外包程序员', cost: 30, desc: '近战 8伤', color: '#94A3B8', img: '/game-assets/tower-outsource.png', tip: '廉价但30%丢空,5%自伤' }, + { type: 'intern', name: '00后实习生', cost: 50, desc: '近战 15伤', color: '#22C55E', img: '/game-assets/tower-intern.png', tip: '整顿职场:5%概率秒杀' }, + { type: 'hrbp', name: 'HRBP', cost: 80, desc: '辅助+20%攻速', color: '#EC4899', img: '/game-assets/tower-hrbp.png', tip: '打鸡血:周围塔攻速+20%' }, + { type: 'ops', name: '运营专员', cost: 90, desc: '溅射 18伤', color: '#8B5CF6', img: '/game-assets/tower-ops.png', tip: '增长黑客:20%双倍HC' }, + { type: 'ppt', name: 'PPT大师', cost: 100, desc: 'AOE减速', color: '#F59E0B', img: '/game-assets/tower-ppt.png', tip: '黑话领域:减速40%' }, + { type: 'senior', name: 'P6资深开发', cost: 120, desc: '远程 30伤', color: '#3B82F6', img: '/game-assets/tower-senior.png', tip: '代码屎山:附带持续伤害' }, + { type: 'pm', name: '产品经理', cost: 160, desc: '需求变更', color: '#06B6D4', img: '/game-assets/tower-pm.png', tip: '需求变更:每4次把怪打回去' }, ] as const type TowerType = 'outsource' | 'intern' | 'hrbp' | 'ops' | 'ppt' | 'senior' | 'pm' +type EffectType = 'attack_boost' | 'speed_boost' | 'money_rain' | 'rage_mode' | 'backfire' + +interface PuaResult { + score: number + title: string + desc: string + effect: EffectType +} + +const EFFECT_META: Record = { + attack_boost: { label: '攻击力提升', color: '#22C55E', icon: '⚔️' }, + speed_boost: { label: '攻速暴增', color: '#FBBF24', icon: '⚡' }, + money_rain: { label: 'HC暴击', color: '#A78BFA', icon: '💰' }, + rage_mode: { label: '全场狂暴', color: '#FF4E00', icon: '🔥' }, + backfire: { label: '废话翻车', color: '#6B7280', icon: '💀' }, +} + +const PUA_PLACEHOLDERS = [ + '今天是本季度最关键的一天,大家冲!', + '不拼搏,对不起父母的养育之恩...', + '996是福报,感恩公司给的机会', + '我们要有狼性精神,卷赢对手!', + '大家加油,相信自己!', +] + +// ── PUA 输入面板 ───────────────────────────────────────────────────────────── +function PuaPanel({ gameReady }: { gameReady: boolean }) { + const [text, setText] = useState('') + const [loading, setLoading] = useState(false) + const [result, setResult] = useState(null) + const [history, setHistory] = useState([]) + const textareaRef = useRef(null) + const placeholder = PUA_PLACEHOLDERS[Math.floor(Math.random() * PUA_PLACEHOLDERS.length)] + + const handleSubmit = useCallback(async () => { + if (!text.trim() || loading || !gameReady) return + setLoading(true) + setResult(null) + try { + const res = await fetch('/api/pua-score', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ text: text.trim() }), + }) + const data: PuaResult = await res.json() + setResult(data) + setHistory(prev => [data, ...prev].slice(0, 5)) + // 通知游戏场景应用 buff + if (typeof window !== 'undefined') { + ;(window as any).__gamePuaBuff?.(data.effect, data.score, data.title) + } + } catch { + setResult({ score: 1, title: '网络故障', desc: 'AI开小差了', effect: 'backfire' }) + } finally { + setLoading(false) + } + }, [text, loading, gameReady]) + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault() + handleSubmit() + } + } + + const scoreColor = (s: number) => + s >= 9 ? '#FF4E00' : s >= 7 ? '#FBBF24' : s >= 4 ? '#22C55E' : '#6B7280' + + return ( +
+ {/* 标题 */} +
+
+ PUA 激励台 +
+
+ 输入打鸡血的话,AI判断鸡血值 +
+
+ + {/* 输入框 */} +