Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

此城有案 · City Mystery

把你身边的真实街区,一键生成一场可步行的实景推理案件。 真实 POI = 线索点 / 案发现场 / 嫌疑人据点;高德规划探案动线;地图即剧本。

一个为高德「智图创想」Skill 创作大赛打造的 LBS × AI 实景剧本杀引擎。给定一个街区中心,引擎从高德拉取真实地点作为舞台,生成一桩内部自洽的推理案件(受害者 / 嫌疑人 / 动机 / 不在场证明 / 环环相扣的线索链 / 真相),用真实步行路线把线索点串成一条探案动线,最终产出一张可交互的暗色「卷宗地图」——线索默认锁定,走到真实地点才解锁


截图 · 玩法流程

真机运行(上海「武康路·安福路」民国谍战案)截取的四个关键状态。暗色民国卷宗美学:案发现场红色信标 · 金色编号线索点 · 步行探案动线 · 自定义信息窗 · 右侧案件档案。

① 初始 · 线索全锁 🔒 ② 解锁中 · 图文联动
初始态 解锁中
进场即上锁,线索默认不可见——走到真实地点才解锁 点线索卡 / 地图点 → 地图飞行定位 + 探案动线逐段点亮 + 打字机揭示线索
③ 指认真凶 ④ 结案 · 推理判定
指认真凶 结案成功
集齐线索后点选嫌疑人指认(红环 ⊙ 已指认),结案按钮亮起 指认正确 → 「推理成功」判定 + 真相还原,可重新探案

玩法闭环

选街区 + 题材
      │
      ▼
高德周边搜真实 POI ──► 分类为 接头点/密信点/落脚点/案发现场
      │
      ▼
案件内核生成案情 ──► 受害者 · 3–4 名嫌疑人(各带动机+不在场证明) · 4 段线索链 · 真相
      │
      ▼
高德步行路线串联 ──► 一条真实可走的探案动线
      │
      ▼
卷宗地图(交互):线索默认 🔒 → 走到地点(点标记/线索卡)→ 🔓 解锁线索
      │
      ▼
集齐线索 → 「提交推理 · 结案」→ 真相大白

线索链按 接头点 →(动机)密信点 →(破不在场证明)落脚点 →(锁手法)案发现场 逐环收口,最终指认唯一真凶。


沉浸式开场 · 语音旁白

进场是一段「云开见日」电影级开场:拨云 → 地图从高空俯冲缩放到案发街区 → 侦探立绘滑入并朗读案情简报,随后才落入可玩的卷宗地图。

  • 语音旁白:开场 4 句案情简报自动朗读,两层方案、可靠优先——
    • 云端情感 TTS(选填):配了 AZURE_SPEECH_KEY 时,构建期(npm start)用 Azure SSML mstts:express-as 情感标签把简报合成带怒吐 / 叹息 / 紧迫语气的 mp3 写入 output/audio/,前端优先播放。
    • 浏览器朗读(兜底):未配 Azure / 音频缺失 / 播放失败 → 自动降级到浏览器 Web Speech 本地音,保证有声。
  • 右下角可静音;旁白进行中可跳过。
  • 性能加固:地图相机移动 / 结案弹窗期间临时关闭玻璃面板的 backdrop-filter,规避 WebGL 地图每帧重算毛玻璃导致的掉帧。

工作原理

阶段 做了什么 关键实现
1. 中心定位 把街区名/坐标解析为中心点 amap.resolveOrigin()(或直接吃 center.coord
2. 勘景 按角色检索词在中心半径内拉真实 POI 并分类、跨类去重 amap.placeAround() × 6(4 角色 + 2 兜底池)
3. 案件生成 纯逻辑、可种子复现,产出案情/人物/线索链/真相 lib/mystery.mjs · generateCase()
4. 探案动线 按线索顺序求真实步行路线(含 polyline) amap.routeWithPolyline(..., "walk") × 3
5. 渲染落盘 注入卷宗模板,产出 case.json + 交互 index.html lib/render.mjs + assets/mystery.html

单次跑通约 10 次高德 API 调用(1 定位 + 6 周边搜 + 3 步行动线)。客户端内置节流(≈3 QPS)与指数退避重试,迁就个人 Key 限流。

角色 → 真实 POI 检索词映射scripts/mystery.mjs):

剧情角色 检索词 线索作用
接头点 ☕ 咖啡馆 受害者死前最后约见
密信点 ✉ 书店 揭示动机 + 凶手姓氏
落脚点 🛎 酒店 打破凶手不在场证明
案发现场 ⚑ 公园 锁定手法与真凶
兜底池 餐厅、茶馆 地点不足时补位

目录结构

amap-citymystery-skill/
├── 启动预览.cmd              # Windows 一键启动器:自动起服务器 + 开浏览器(调用 scripts/preview.ps1)
├── scripts/
│   ├── mystery.mjs           # 编排器:定位→勘景→生成→动线→落盘(I/O 层)
│   ├── serve.mjs             # 零依赖本地预览服务器
│   ├── preview.ps1           # 一键预览逻辑(PowerShell,由 启动预览.cmd 调用)
│   └── lib/
│       ├── mystery.mjs       # 案件生成内核(纯逻辑·可种子复现·题材包 THEMES)
│       ├── amap.mjs          # 高德 Web 服务客户端(节流/重试/不回显 Key)
│       ├── render.mjs        # 把案件数据 + JS Key 注入卷宗模板
│       ├── tts.mjs           # 云端情感 TTS(Azure SSML 合成开场旁白,仅服务端构建期)
│       ├── config.mjs        # Key 解析与状态(只暴露 ✅/❌,绝不打印明文)
│       └── geo.mjs           # 纯几何工具(haversine 等)
├── assets/
│   ├── mystery.html          # 卷宗地图模板(暗色美学 + 线索解锁交互 + 电影开场)
│   └── narrator.png          # 侦探旁白立绘
├── input/
│   ├── sample-mystery.json   # 样例输入(武康路·安福路民国谍战案)
│   └── sample-modern.json    # 样例输入(陆家嘴现代追凶案)
├── output/                   # 产物(gitignored):case.json + index.html + audio/
├── artifacts/                # 截图等临时产物(gitignored)
├── docs/screenshots/         # README 用四态截图(入库)
├── .env.example              # 环境变量模板(复制为 .env 填真值)
└── package.json

快速开始

1. 配置高德 Key

cp .env.example .env

编辑 .env,填入你的高德 Key(申请:https://console.amap.com/dev/key/app):

  • AMAP_WEB_SERVICE_KEY必填)— 服务平台选「Web 服务」。用于服务端选点与路线。
  • AMAP_JS_API_KEY(选填)— 服务平台选「Web 端(JS API)」。交互地图渲染需要。
  • AMAP_JS_SECURITY_CODE(选填)— 与 JS API Key 配套的安全密钥。

云端情感 TTS(选填,让开场旁白真有怒吐/叹息语气)——申请:https://portal.azure.com 创建「语音服务(Speech)」资源:

  • AZURE_SPEECH_KEY — Azure 语音服务密钥。服务端密钥,仅构建期使用,绝不写进产物/日志
  • AZURE_SPEECH_REGION — 资源区域,如 eastasia(不填默认 eastasia)。
  • AZURE_SPEECH_VOICE — 朗读音色,不填默认 zh-CN-YunxiNeural(多情感男声侦探腔)。

不配 Azure 也能跑:开场旁白自动降级为浏览器本地朗读(见「沉浸式开场 · 语音旁白」)。

Key 也可放在 ~/.openclaw/openclaw.jsonskills.entries["amap-citymystery"].env 下(OpenClaw Skill 形态)。

2. 生成案件

npm start          # 或 npm run mystery(同一命令的别名)
# 等价于:node scripts/mystery.mjs --input input/sample-mystery.json --output-dir output

控制台会打印 Key 状态、勘景数量、案情摘要与真相剧透;产物写入 output/case.jsonoutput/index.html

自定义街区:复制 input/sample-mystery.json 改参数,再 node scripts/mystery.mjs --input <你的输入>.json --output-dir output

想跑现代追凶题材:npm run mystery:modern(用 input/sample-modern.json,上海陆家嘴实时路况案)。两个题材共用同一套生成/预览流程。

3. 预览交互地图

Windows 一键(推荐):双击项目根目录的 启动预览.cmd —— 自动起本地服务器并打开浏览器,省去手敲命令。

或手动(全平台):

npm run serve
# 打开 http://localhost:8791/

⚠️ 别直接双击 / file:// 打开 index.html——高德会因域名非法触发 INVALID_USER_DOMAIN(这正是「双击网页黑屏/一堆报错」的根源)。请用上面任一方式经 localhost 访问,并在高德 JS Key 的域名白名单里加 localhost。未配置 JS Key 时页面自动降级为「卷宗未就绪」占位,右侧案件档案仍可查阅。


输入格式(input/sample-mystery.json

{
  "area": "武康路·安福路",                 // 街区名(用于标题)
  "center": { "name": "上海武康大楼", "city": "上海" }, // 或 { "name":"...", "coord":[lng,lat] }
  "radius": 1100,                          // 周边搜索半径(米),默认 1200
  "theme": "noir1930",                     // 题材 key(见「题材系统」)
  "suspects": 3,                            // 嫌疑人数 3–4
  "seed": 1937,                             // 可选;同 seed 复现同一案件
  "language": "zh-CN"                       // 预留
}

输出格式(output/case.json

{
  "theme":   { "key", "label", "kicker" },
  "area":    "武康路·安福路",
  "title":   "此城有案 · 武康路·安福路疑案",
  "timeText":"案发于一个雷雨交加的午夜",
  "victim":  { "name", "role" },
  "suspects":[ { "id", "name", "role", "motive", "alibi" } ],
  "scenes":  [ { "order", "kind", "role", "icon", "name", "type", "address",
                 "lng", "lat", "rating", "isCrimeScene?",
                 "clue": { "title", "text", "unlocks" } } ],
  "solution":{ "culpritId", "culpritName", "culpritRole", "method", "motive", "evidence", "reveal" },
  "center":  { "label", "lng", "lat" },
  "route":   { "legs":[ { "fromOrder","toOrder","polyline","durationMinutes","distanceMeters" } ],
               "totalMinutes", "totalMeters" },
  "difficulty": 3,
  "seed": 1937,
  "spanMeters": 0,
  "assumptions": [ /* 虚构声明 / Skill 接管说明 / 评分透传声明 */ ],
  "meta": { "apiCalls", "generatedAt", "poolSize" }
}

现代追凶(theme:"modern")的 case.json 还会多出:顶层 window(案发窗口分钟)与 theme.mode:"drive";每个 suspect 增加 place{name,lng,lat}driveMinutesdistanceMeterstraffic{label,congestedRatio}cleareddrivePolyline


题材二 · 现代追凶(实时路况即裁判)

theme: "modern" 把「实时路况下驾车赶不赶得到」做成真实的不在场证据 —— 这是本 Skill 与地图耦合最深的玩法:

  • 每名嫌疑人落在一个真实据点(中心四周环形撒点搜到的小区/公寓);系统用高德 /v5/direction/drivingshow_fields=cost,polyline,tmcs)算出实时路况感知的驾车耗时 + 逐段拥堵到案发现场。
  • 线索锁定案发窗口 W 分钟;驾车耗时 > W = 洗清(赶不到),≤ W = 无法排除
  • 真凶在「赶得到」的人里,再由现场物证 / 动机锁定。距离更近 ≠ 更快——一路畅通的远点可能反超堵在路上的近点,这正是实时路况带来的反直觉破绽。

高德「交通态势」矩形 API 已于 2020 年底下线、新 Key 不可用;故改用驾车接口自带的实时耗时 + tmcs 逐段路况作为路况源(可用且更贴合具体路线)。

真机样例npm run mystery:modern,上海陆家嘴,seed 2026,案发窗口 14 分钟):

嫌疑人 角色 据点 实时驾车 路况 裁定
赵薇 贴身助理 斜桥公寓 12 min 全程畅通 ⚠️ 无法排除 → 真凶
高岚 公司法务总监 永年路24弄 14 min 全程畅通 ⚠️ 无法排除
孙骁 私人健身教练 杨高南路1677弄 16 min 全程畅通 ✅ 已洗清
王珂 前妻 河间路50弄 19 min 全程畅通 ✅ 已洗清

16/19 分钟的两人被实时路况「洗清」(赶不到);剩下 12/14 分钟两人,再由现场物证与动机锁定真凶。地图上会画出每个嫌疑人据点 → 现场的实时驾车路线(有作案时间=红、已洗清=灰),点嫌疑人即高亮其路线。

运行:npm run mystery:modernnpm run serve → 打开 http://localhost:8791/


题材系统

题材即世界观,集中在 lib/mystery.mjsTHEMES。现内置 noir1930 民国谍战(步行探案)与 modern 现代追凶(实时路况驾车不在场),各含姓名池、身份/动机/手法/物证模板、场景类型与图标。

扩展新题材:照 noir1930 的结构新增一个 key(如 wuxia 武侠、scifi 近未来),无需改编排器或模板:

export const THEMES = {
  noir1930: { /* ... */ },
  wuxia: {
    key: "wuxia", label: "江湖疑案", kicker: "某年 · 某镇",
    surnames: [...], given: [...],
    victimRoles: [...], suspectRoles: [...],
    motives: [...], redHerrings: [...],
    methods: ["在『{scene}』...被一招..."], evidences: [...],
    sceneKinds: { meet:{label,icon}, archive:{...}, lodging:{...}, scene:{...} },
  },
};

作为 OpenClaw / ClawHub Skill

本仓库是「技能提供能力、宿主大模型提供创意」的范例:

  • 技能负责确定性、可复现的硬骨头:高德选点 + 真实动线 + 卷宗渲染 + 结构化提示词契约
  • 「现写案情」这件最需要创造力的事,由宿主大模型case.json 的结构接管,可生成远比内置模板丰富的剧本。
  • 内置 generateCase() 是一个可离线复现的程序化底座:没有大模型也能跑出一桩自洽的 demo 案件(同 seed 必复现)。

安全与合规边界

  • 🔑 Key 不入库不入产物.env 已 gitignore;Web 服务 Key 仅服务端使用,绝不写进 HTML/日志/异常;仅前端 JS API Key 按高德设计注入页面。
  • 🎭 纯虚构声明:案情、人物、线索均为程序化生成的虚构剧情;真实地点仅作剧本舞台,与现实场所无任何影射(已写入 case.json.assumptions)。
  • 📊 数据不编造:地点评分等字段直接透传高德,缺失则标注,不臆造。

大赛信息

  • 赛事:高德「智图创想」Skill 创作大赛
  • 评分:创新性 + 场景可行性,完整 demo 加分
  • 定位:剧本杀 × CityWalk × LBS × AI 的实景推理体验,区别于「工具型」地图应用

License

ISC

About

把真实街区一键生成可步行的实景推理剧本杀:真实 POI 当线索点、高德规划探案动线、走到地点 GPS 解锁、一键唤起高德导航。LBS × AI 高德开放平台 Skill(智图创想 Skill 创意征集赛)。

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages