Skip to main content
Glama
README.md
<div align="center">

# 麦仔 1024

<img src="docs/banner.svg" alt="麦仔 1024 头像示例" width="720">

**用你的麦当劳订单,抽一枚限量像素朋克食物头像**

一共 1024 枚 · 一笔订单换一次抽卡 · 吃得越多越容易出稀有

[![在线试玩](https://img.shields.io/badge/在线试玩-免登录-F7C948?style=flat-square)](https://shellteo.github.io/mcd-nft/)
[![CI](https://github.com/shellteo/mcd-nft/actions/workflows/ci.yml/badge.svg)](https://github.com/shellteo/mcd-nft/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/v/release/shellteo/mcd-nft?style=flat-square&color=C9483B)](https://github.com/shellteo/mcd-nft/releases)
![Node](https://img.shields.io/badge/Node.js-20%2B-3C873A?style=flat-square)
![依赖](https://img.shields.io/badge/依赖-零-2E7D3A?style=flat-square)
![MCP](https://img.shields.io/badge/麦当劳_MCP-Streamable_HTTP-8A4FD8?style=flat-square)
![License](https://img.shields.io/badge/License-MIT-9C8B78?style=flat-square)

**[在线试玩(30 秒,免登录)](https://shellteo.github.io/mcd-nft/) · [快速开始](#快速开始) · [在 WorkBuddy 里玩](#在-workbuddy-里玩) · [常见问题](#常见问题)**

</div>

---

麦仔 1024 是一套共 **1024 枚** 的食物拟人像素头像(10 月 24 日程序员节)。每枚的样子在开抽前就由固定种子定好,和链上 PFP 头像系列一样:编号唯一、属性组合唯一、抽走一枚就少一枚。

它通过麦当劳中国官方 MCP 读取**你自己的**订单:

- **一笔已完成订单 = 一张抽卡券。** 取消、未支付的订单不算,同一笔订单只能用一次。
- **吃得越多,越容易出稀有。** 最近实付和单数算成「幸运值」,稀有、史诗、传说、隐藏款的权重跟着变高。
- **常吃什么,就容易抽到谁。** 薯条点得多,薯条仔的权重就高;常点汉堡,汉堡仔就多。
- **什么时候吃,也有影响。** 常在夜里点单的人,更容易抽到「夜宵深蓝」背景。
- **抽卡前公示概率。** 每一档你的实际概率和不加成时的概率并排列出来,抽完的档位归零。

麦当劳程序员节创意开发大赛的非官方参赛作品。抽卡免费,不收钱、不连钱包、不上链、不能转让,头像没有现金价值。

![抽卡页面:读取订单后看到自己的幸运值和各档概率,点「抽一次」开盒](docs/screenshot.png)

## 目录

- [四种玩法](#四种玩法)
- [快速开始](#快速开始)
- [六个角色](#六个角色)
- [稀有度和概率](#稀有度和概率)
- [在 WorkBuddy 里玩](#在-workbuddy-里玩)
- [命令行抽卡](#命令行抽卡)
- [限量怎么保证](#限量怎么保证)
- [隐私和安全](#隐私和安全)
- [常见问题](#常见问题)
- [开发](#开发)
- [参赛文件](#参赛文件)
- [更新记录](#更新记录)

## 四种玩法

| 玩法 | 适合 | 需要什么 |
|---|---|---|
| [在线试玩](https://shellteo.github.io/mcd-nft/) | 先看看好不好玩 | 一个浏览器。用两个示例会员抽,不需要 Token |
| 本地网页 | 用自己的订单抽 | Node.js 20+ 和麦当劳 MCP Token |
| [WorkBuddy Skill](#在-workbuddy-里玩) | 在 AI 对话里说一句「抽麦仔」 | WorkBuddy 和 mcd-mcp 连接器 |
| [命令行](#命令行抽卡) | 喜欢终端,或者想接进别的工具 | Node.js 20+ 和 `order-list` 的返回 |

四种玩法用的是同一套代码:同一个 1024 枚系列、同一套概率、同一份像素画。

## 快速开始

### 30 秒在线试玩

打开 **https://shellteo.github.io/mcd-nft/**,点「汉堡控」或「薯条夜猫子」,再点「抽一次」。

在线版完全在浏览器里运行,不连麦当劳的服务器。每位访客有自己的一池 1024 枚,结果只留在这个页面里。

### 用自己的订单

需要 Node.js 20 或更新版本,不需要安装依赖。

```bash
git clone https://github.com/shellteo/mcd-nft.git
cd mcd-nft
npm start
```

1. 浏览器打开 `http://127.0.0.1:8787`。
2. 在 [open.mcd.cn/mcp](https://open.mcd.cn/mcp) 登录并申请 MCP Token,贴进输入框,点「读取我的订单」。
3. 看看自己的幸运值和各档概率,点「抽一次」。
4. 抽到的头像可以下载 SVG 和元数据 JSON。

服务只监听本机。Token 只用来读一次订单,不会写进文件。

## 六个角色

<table>
  <tr>
    <td align="center"><img src="docs/species-fries.svg" width="120" alt="薯条仔"><br><b>薯条仔</b><br>薯条</td>
    <td align="center"><img src="docs/species-burger.svg" width="120" alt="汉堡仔"><br><b>汉堡仔</b><br>汉堡、卷</td>
    <td align="center"><img src="docs/species-nugget.svg" width="120" alt="鸡块仔"><br><b>鸡块仔</b><br>麦乐鸡、鸡翅、小食</td>
  </tr>
  <tr>
    <td align="center"><img src="docs/species-cola.svg" width="120" alt="可乐仔"><br><b>可乐仔</b><br>可乐、咖啡、茶饮</td>
    <td align="center"><img src="docs/species-cone.svg" width="120" alt="甜筒仔"><br><b>甜筒仔</b><br>甜筒、麦旋风、派、圣代</td>
    <td align="center"><img src="docs/species-muffin.svg" width="120" alt="麦满分仔"><br><b>麦满分仔</b><br>麦满分、猪柳、豆浆等早餐</td>
  </tr>
</table>

每个角色下面写的是给它加成的餐品。

每枚头像是一张 40 × 40 的像素画,风格参考链上朋克头像:半身像、纯色背景、面无表情,再配上莫西干、反戴帽、眼罩、耳钉和金链子。每枚由 8 层叠成:角色、背景、配色、头饰、眼睛、嘴巴、脸部、配件。一共 60 多个属性,每个属性有最低档位,例如「薯条王冠」「激光眼」「钻石大金链」只出现在传说款以上。

## 稀有度和概率

<table>
  <tr>
    <td align="center"><img src="docs/tier-common.svg" width="120" alt="普通款示例"><br><b>普通</b><br>640 枚 · 62.5%</td>
    <td align="center"><img src="docs/tier-rare.svg" width="120" alt="稀有款示例"><br><b>稀有</b><br>260 枚 · 25.4%</td>
    <td align="center"><img src="docs/tier-epic.svg" width="120" alt="史诗款示例"><br><b>史诗</b><br>100 枚 · 9.8%</td>
    <td align="center"><img src="docs/tier-legendary.svg" width="120" alt="传说款示例"><br><b>传说</b><br>20 枚 · 1.95%</td>
    <td align="center"><img src="docs/tier-mythic.svg" width="120" alt="隐藏款 #1024"><br><b>隐藏</b><br>4 枚 · 0.39%</td>
  </tr>
</table>

百分比是不加成时的概率。普通款只有基础属性;稀有款带 1 个稀有属性,史诗款带 2 个史诗属性,传说款带 3 个传说属性,所以越稀有,样子越不一样。

隐藏款是 4 枚手绘的独一无二款,上图是其中的 **#1024「程序员节 · 键帽汉堡」**,固定在最后一号。另外 3 枚不剧透。

### 抽卡怎么算

每枚还没被抽走的头像都有一个权重,抽卡时按权重随机挑一枚:

```
权重 = 档位加成 × 角色加成 × 时段加成

幸运值 L  = 0.8 × min(1, 最近实付 / 500 元) + 0.2 × min(1, 完成单数 / 10)
档位加成  = 普通 1,稀有 1 + 1.5L,史诗 1 + 4L,传说 1 + 8L,隐藏 1 + 12L
角色加成  = 1 + 3 × 这类餐品占你点过的餐品的比例
时段加成  = 背景和你最常点单的时段一致时 ×1.6
```

举个例子:示例会员「薯条夜猫子」最近实付 ¥561.50、9 单,幸运值 98。传说款的概率从不加成时的 1.95% 变成 8.59%,薯条仔的权重是 ×2.71。

页面会在抽卡前算好并列出你在每个档位的实际概率。一个档位抽完以后,它的概率对所有人都变成 0。随机数来自 WebCrypto 的 `crypto.getRandomValues`。

## 在 WorkBuddy 里玩

麦仔 1024 自带一个 WorkBuddy Skill:`.workbuddy/skills/maizai-1024/SKILL.md`。装好以后直接在对话里说「帮我抽一个麦仔」,WorkBuddy 会用 mcd-mcp 连接器读你的订单,再调用本项目的抽卡脚本,把抽到的像素头像发回对话里。Token 只放在 WorkBuddy 的连接器配置里,本项目的代码碰不到。

1. 按官方说明在 WorkBuddy 里启用 mcd-mcp 连接器:「专家·技能·连接器 → 连接器 → 自定义连接器 → 配置 MCP」,地址 `https://mcp.mcd.cn`,填入自己的 Token。
2. 用 WorkBuddy 打开本项目文件夹,项目里的 `.workbuddy/skills/` 会作为项目级 Skill 生效。也可以把 `maizai-1024` 整个文件夹复制到 `~/.workbuddy/skills/` 全局使用,这时要告诉 WorkBuddy 项目放在哪里。
3. 在对话里说「抽麦仔」「看看我的出货概率」或「我抽到过哪些麦仔」。

Skill 只调用只读工具 `order-list`、`query-order`、`query-my-account`,不会下单、领券或兑换积分。

WorkBuddy 版本之间 Skill 的目录约定可能不同,以你安装的版本的说明为准。

## 命令行抽卡

Skill 背后是一个不依赖 WorkBuddy 的脚本,也可以自己在终端里用。把 mcd-mcp 的 `order-list` 原始返回存成 JSON 文件,然后:

| 命令 | 作用 |
|---|---|
| `node scripts/draw.mjs odds --orders <文件>` | 看幸运值、抽卡券和各档概率 |
| `node scripts/draw.mjs draw --orders <文件> --times 3` | 抽 3 次(最多 10 次) |
| `node scripts/draw.mjs mine --orders <文件>` | 看这批订单抽到过哪些 |
| `node scripts/draw.mjs draw --demo fries` | 用示例会员抽,不需要订单文件 |

抽到的头像会存成 PNG、SVG 和元数据 JSON,放在 `maizai-out/`。加 `--json` 输出机器可读的结果,`--account <文件>` 可以带上 `query-my-account` 的返回。

命令行和网页共用同一个账本 `data/registry.json`,同一张抽卡券在哪边都只能用一次。

## 限量怎么保证

整套 1024 枚由固定种子 `maizai-1024/v1` 生成,每次启动都一样。抽卡记录写在 `data/registry.json`:一个编号只能被抽一次,一张抽卡券(订单的哈希)只能用一次。网页和命令行每次抽卡前都会重新读一遍账本,两边同时抽也不会发出同一枚。

账本在运行服务的这台机器上。多人共用一个 1024 池,就把服务部署在一台共享的机器上。元数据按 ERC-721 常见的 `name / description / image / attributes` 格式输出,以后想上链也能直接用;本项目不做上链。

## 隐私和安全

- **只读。** 只调用 `order-list`、`query-order`、`query-my-account`、`query-my-coupons`、`mall-order-list`、`now-time-info`,从不下单、取消、领券、兑换积分或抽奖。
- **Token 不落地。** 本地网页的 Token 只在一次请求的内存里,读完订单就丢掉;WorkBuddy 里 Token 只在连接器配置里。
- **只留哈希。** 账本只保存订单哈希、编号和时间,不存订单号、电话、地址或门店。
- **只听本机。** 本地服务只监听 `127.0.0.1`。
- **在线版不碰订单。** 在线试玩只用内置的示例会员,不会请求麦当劳的接口。

## 常见问题

**这是 NFT 吗?要钱吗?**
样子和玩法学的是链上 PFP 头像:总量固定、编号唯一、按稀有度分档。但它不上链、不连钱包、不能交易,抽卡完全免费,头像没有现金价值。

**为什么在线试玩不能用我自己的订单?**
在线版是放在 GitHub Pages 上的静态网页,没有服务器替你去连麦当劳的接口,也不应该让你把 Token 贴进网页。用自己的订单,请在本地运行,或者用 WorkBuddy。

**怎样更容易抽到稀有款?**
最近实付越高、完成单数越多,幸运值越高,最高 100。幸运值是 100 时,传说款的概率是不加成时的 4 倍多。想要某个角色,就多点对应的餐品。

**能不能反复抽?**
一笔已完成订单只能换一张券,用完就没了。重新读取订单不会多出抽卡次数,下一笔订单完成后才会多一张。

**金额为什么和 App 里不一样?**
`order-list` 目前只返回最近大约 10 笔订单,没有分页。幸运值用的是这些订单里已完成的实付,不是终身消费。

**1024 枚抽完了怎么办?**
抽完就没有了,这正是限量的意思。之后再抽会提示「1024 枚已经全部抽完」。

## 开发

```bash
npm test                          # 单元测试
node scripts/build-pages.mjs      # 重新生成在线试玩页(docs/)
node scripts/readme-images.mjs    # 重新生成 README 里的图片(docs/)
PORT=9000 MAIZAI_DATA=/tmp/maizai.json npm start   # 换端口或账本位置
```

改了 `public/`、`pages/` 或 `src/` 里共享的模块后,记得运行前两个脚本。CI 会检查 `docs/` 是否和源码一致。

| 目录 | 内容 |
|---|---|
| `src/` | 订单整理、系列生成、概率、账本、像素画、本地服务。除 `server.mjs`、`registry.mjs`、`png.mjs`、`mcp.mjs` 外都能直接在浏览器里运行 |
| `public/` | 抽卡页面 |
| `pages/` | 在线试玩版的浏览器端接口 |
| `scripts/` | 命令行抽卡、生成在线试玩页、生成 README 图片 |
| `docs/` | GitHub Pages:在线试玩页和 README 图片(生成文件) |
| `.workbuddy/skills/maizai-1024/` | WorkBuddy Skill |

## 目标用户

开通了麦当劳中国 MCP 的会员,想用自己的点餐记录换一枚好玩的头像,留着当头像,或者跟朋友比谁抽到的更稀有。

## 参赛文件

| 文件 | 作用 |
|---|---|
| `README.md` | 介绍、安装、示例、目标用户 |
| `CONTEST_DECLARATION.md` | 官方参赛声明,内容未改 |
| `MCP_INTEGRATION.md` | 用了哪些 MCP Tool、调用顺序、业务价值 |
| `mcp-config.example.json` | 脱敏配置,Token 用 `${MCD_MCP_TOKEN}` 占位 |
| `src/`、`public/`、`pages/`、`scripts/` | 源代码 |
| `.workbuddy/skills/maizai-1024/SKILL.md` | WorkBuddy Skill |

## 更新记录

见 [CHANGELOG.md](CHANGELOG.md)。

## 许可

MIT。麦当劳相关商标归权利人所有。本项目没有使用麦当劳官方标志,所有角色和图案都是原创像素画。

## Star History

[![Star History Chart](https://api.star-history.com/svg?repos=shellteo/mcd-nft&type=Date)](https://star-history.com/#shellteo/mcd-nft&Date)