diff --git a/docs/csv.md b/docs/csv.md new file mode 100644 index 0000000..8d860ea --- /dev/null +++ b/docs/csv.md @@ -0,0 +1,432 @@ +# CSV 编写说明 + +TTRPG Tools 使用带有 YAML Front Matter 的 CSV 格式来定义卡牌/表格数据。这种格式支持在 CSV 文件头部定义元数据、字段配置和共享属性。 + +## 文件结构 + +```csv +--- +# YAML Front Matter(可选) +fields: + - name: 字段名 + description: 字段描述 +deck: + size: 54x86 + grid: 5x8 +shared_prop: 共享属性值 +--- +# CSV 数据 +label,body,field1,field2 +1,内容,值 1,值 2 +``` + +--- + +## YAML Front Matter + +Front Matter 是可选的,位于文件开头,使用 `---` 包裹的 YAML 格式内容。 + +### 基本结构 + +```yaml +--- +fields: + - name: name + description: 卡牌名称 + - name: effect + description: 效果描述 + examples: ["造成 5d6 伤害", "恢复 2d4 生命值"] +deck: + size: 63x88 + grid: 5x5 + bleed: 1 + padding: 2 +custom_prop: 自定义属性 +--- +``` + +### Front Matter 属性 + +#### `fields` - 字段定义 + +定义 CSV 中各列的含义和配置。 + +```yaml +fields: + - name: name # 字段名称(英文,用于 CSV 列名) + description: 卡牌名称 # 字段描述 + examples: # 示例值列表(可选) + - 火球术卷轴 + - 治疗药水 + dataType: word # 数据类型(可选):word/paragraph/number/symbol/symbol_list + function: identify # 字段功能(可选):identify/compare/flavor/rule +``` + +**数据类型说明:** + +| 类型 | 说明 | 示例 | +|------|------|------| +| `word` | 词语 - 短文本 | "火球术"、"战士"、"传说" | +| `paragraph` | 文本段落 - 长描述 | "造成 5d6 点火焰伤害,范围内的所有生物进行敏捷豁免" | +| `number` | 数字 - 可比较的数值 | 3、5、10 | +| `symbol` | 单一符号 - 图标/标记 | 🔥、⚔️、🛡️ | +| `symbol_list` | 符号列表 - 多个图标/标记 | 🔥🔥🔥、⚔️🛡️💫 | + +**字段功能说明:** + +| 功能 | 说明 | 示例 | +|------|------|------| +| `identify` | 辨识 - 用于识别卡牌身份 | 名称、编号、唯一标识 | +| `compare` | 比较 - 用于游戏机制中的数值比较 | 费用、攻击力、防御力 | +| `flavor` | 游戏风味描述 - 提供背景故事/氛围 | 背景故事、引言、lore | +| `rule` | 规则确认 - 明确游戏规则相关的信息 | 效果文本、触发条件、限制 | + +#### `deck` - 卡牌显示配置 + +用于 `:md-deck` 组件的默认配置。 + +```yaml +deck: + size: 63x88 # 卡牌尺寸(mm),格式 "宽 x 高" + grid: 5x5 # 网格布局,格式 "列 x 行" + bleed: 1 # 出血边距(mm) + padding: 2 # 内边距(mm) + shape: rectangle # 卡牌形状:rectangle/circle/hex/diamond + layers: "name:1-2,14" # 图层配置(可选) + back_layers: "" # 背面图层配置(可选) +``` + +图层配置语法遵循`layer:1,1-5,8f8s`格式: +- `layer`:图层名称,多个图层用空格分隔 +- `1,1-5,8`:网格范围,覆盖卡牌排版网格中的指定区域。 +- `f8`:字体大小,使用8mm字体。可选。 +- `s`:图层朝向,有nwse东西南北四种方向,描述文字上侧的朝向,默认为n北方。 + +#### 自定义属性 + +可以在 Front Matter 中定义任意自定义属性,这些属性会被自动应用到所有卡牌行。 + +```yaml +--- +game_system: 龙与地下城 5e +author: DM_Name +version: 1.0 +--- +``` + +--- + +## CSV 数据部分 + +### 基本格式 + +```csv +label,body,field1,field2 +1,内容,值 1,值 2 +``` + +### 必需列 + +#### `label` + +每行的唯一标识,用于查找和修改卡牌。 + +- 可以是数字:`1`, `2`, `3` +- 可以是文本:`card_001`, `npc_villager` +- 支持分组前缀:`forest_1`, `dungeon_1` + +#### `body` + +卡牌的主要内容,支持 Markdown 格式。 + +```csv +label,body +1,"### 标题 + +**加粗文本** + +- 列表项 1 +- 列表项 2" +``` + +### 可选列 + +除 `label` 和 `body` 外,可以定义任意自定义列,列名在 Front Matter 的 `fields` 中定义。 + +```csv +label,body,name,type,cost,effect +1,完整内容,火球术,法术,3,造成 5d6 火焰伤害 +``` + +--- + +## 变量语法 + +在 `body` 列或 Front Matter 中,可以使用 `{{prop}}` 语法引用其他列的值或 Front Matter 中的属性。 + +### 引用同行其他列 + +```csv +label,body,adj,noun +1,"**{{adj}}** 的{{noun}}",高大,战士 +2,"{{adj}}的{{noun}}",矮小,法师 +``` + +渲染后: +- 第 1 行:**高大** 的战士 +- 第 2 行:矮小的法师 + +### 引用 Front Matter 属性 + +```yaml +--- +game_system: D&D 5e +author: DM_Name +--- +``` + +```csv +label,body +1,"这是一个{{game_system}}冒险,由{{author}}创建" +``` + +渲染后:这是一个 D&D 5e 冒险,由 DM_Name 创建 + +### 随机引用(remix 模式) + +在 `:md-table` 组件中,可以使用 `remix` 属性启用随机引用: + +```markdown +:md-table[./data.csv]{remix=true} +``` + +此时 `{{prop}}` 会从所有行中随机选择一行的值,而不是使用当前行的值。 + +--- + +## 分组支持 + +可以使用额外的列来对数据进行分组。 + +```csv +group,label,body +森林小径,1,狼群袭击 +森林小径,2,荆棘陷阱 +森林小径,3,迷路 +塔楼楼梯,1,台阶坍塌 +塔楼楼梯,2,落石 +``` + +在 `:md-table` 组件中,可以使用 `group` 属性来筛选显示特定组的数据: + +```markdown +:md-table[./encounters.csv]{group="森林小径"} +``` + +--- + +## 特殊字符处理 + +### 包含逗号的值 + +使用双引号包裹: + +```csv +label,body +1,"Hello, world" +``` + +### 包含双引号的值 + +使用两个双引号转义: + +```csv +label,body +1,"他说:""你好""" +``` + +### 包含换行的值 + +使用双引号包裹,直接写入换行: + +```csv +label,body +1,"### 标题 + +这是第一段 + +这是第二段" +``` + +### 包含 `#` 注释 + +CSV 解析器会将 `#` 开头的行视为注释,但如果 `#` 在双引号内则正常处理: + +```csv +label,body +1,"# 这不是注释" +# 这是真正的注释 +``` + +--- + +## 完整示例 + +### 示例 1:魔法物品卡牌组 + +```csv +--- +fields: + - name: name + description: 物品名称 + dataType: word + function: identify + - name: type + description: 物品类型 + dataType: word + function: identify + examples: [武器,防具,饰品,消耗品] + - name: rarity + description: 稀有度 + dataType: word + function: compare + examples: [普通,稀有,史诗,传说] + - name: cost + description: 价格 + dataType: number + function: compare + - name: effect + description: 效果描述 + dataType: paragraph + function: rule +deck: + size: 63x88 + grid: 5x5 + shape: rectangle +game_system: D&D 5e +--- +label,body,name,type,rarity,cost,effect +1,"### 火球术卷轴 + +一张泛黄的羊皮纸,上面绘有火焰符文。",火球术卷轴,消耗品,稀有,150,"投掷后对 20 尺内所有生物造成 8d6 火焰伤害(敏捷豁免减半)" +2,"### 治疗药水 + +红色液体在玻璃瓶中翻滚,散发着温暖的光芒。",治疗药水,消耗品,普通,50,"饮用后恢复 2d4+2 点生命值" +3,"### +1 长剑 + +剑刃闪烁着微光,握柄包裹着精致的皮革。",+1 长剑,武器,稀有,300,"攻击和伤害检定 +1" +``` + +### 示例 2:随机遭遇表 + +```csv +--- +fields: + - name: group + description: 遭遇地点分组 + - name: title + description: 遭遇标题 + - name: type + description: 遭遇类型 +deck: + size: 54x86 + grid: 4x6 +--- +group,label,body,title,type +森林小径,1,"### 狼群袭击 + +三只灰狼从灌木丛中扑出。 + +**交互**:战斗(3 只巨狼)或逃脱(敏捷检定 3/2) + +**风险**:失败受到 d8 撕咬伤害",狼群袭击,战斗 +森林小径,2,"### 荆棘陷阱 + +带刺藤蔓缠住你的双腿。 + +**交互**:挣脱(力量检定 3/2) + +**风险**:失败受到 1 点体质损伤",荆棘陷阱,陷阱 +森林小径,3,"### 迷路 + +浓雾弥漫,你失去了方向。 + +**交互**:寻找路径(感知检定 3/2) + +**风险**:失败受到 1 点感知损伤",迷路,事件 +塔楼楼梯,1,"### 台阶坍塌 + +脚下的石阶突然碎裂。 + +**交互**:保持平衡(敏捷检定 3/1) + +**风险**:失败受到 1 点敏捷损伤",台阶坍塌,陷阱 +``` + +### 示例 3:NPC 名录 + +```csv +--- +fields: + - name: name + description: NPC 名称 + - name: role + description: 角色定位 + - name: location + description: 出现地点 +deck: + size: 54x86 + grid: 4x6 +--- +label,body,name,role,location +1,"### 老妇人玛拉 + +**类型**:信息提供者/商人 + +**位置**:村庄入口 + +**出售**:草药、治疗药水 + +**情报**:知道关于森林的警告",老妇人玛拉,商人,村庄入口 +2,"### 护卫队长塞里克 + +**类型**:任务发布者 + +**位置**:酒馆二楼 + +**任务**:调查废弃庄园 + +**秘密**:知道血契的真相",塞里克,任务发布者,酒馆 +``` + +--- + +## 与组件集成 + +### `:md-deck` 组件 + +```markdown +:md-deck[./magic-items.csv]{size="63x88" grid="5x5" roll=true} +``` + +- `roll=true`:添加随机抽卡按钮 + +### `:md-table` 组件 + +```markdown +:md-table[./encounters.csv]{group="森林小径" remix=true} +``` + +- `group`:筛选特定组的数据 +- `remix=true`:启用随机引用模式 + +--- + +## 最佳实践 + +1. **使用有意义的 label**:便于后续查找和修改 +2. **在 Front Matter 中定义所有字段**:便于工具理解和处理 +3. **使用 `{{prop}}` 变量**:减少重复内容,支持动态组合 +4. **合理使用分组**:便于筛选和管理大量数据 +5. **保持 body 的 Markdown 格式一致**:确保渲染效果统一 +6. **为字段添加 `examples`**:帮助 AI 工具生成内容时参考 diff --git a/src/cli/commands/mcp.ts b/src/cli/commands/mcp.ts index a58d6a2..ae25156 100644 --- a/src/cli/commands/mcp.ts +++ b/src/cli/commands/mcp.ts @@ -90,111 +90,6 @@ async function mcpServeAction(host: string, options: MCPOptions) { server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ - { - name: 'generate_card_deck', - description: '生成 TTRPG 卡牌组内容,包括 Markdown 介绍文件、CSV 数据文件和 md-deck 组件配置(一站式快捷工具)', - inputSchema: { - type: 'object', - properties: { - deck_name: { - type: 'string', - description: '卡牌组名称,将用于生成文件名', - }, - output_dir: { - type: 'string', - description: '输出目录路径(相对路径相对于 MCP 服务器工作目录)', - }, - card_count: { - type: 'number', - description: '卡牌数量', - default: 10, - }, - card_template: { - type: 'object', - description: '卡牌模板定义', - properties: { - fields: { - type: 'array', - description: '卡牌字段列表', - items: { - type: 'object', - properties: { - name: { - type: 'string', - description: '字段名称(英文,用于 CSV 列名)', - }, - description: { - type: 'string', - description: '字段描述', - }, - examples: { - type: 'array', - description: '示例值列表', - items: { type: 'string' }, - }, - }, - required: ['name'], - }, - }, - examples: { - type: 'array', - description: '完整的卡牌示例数据', - items: { - type: 'object', - additionalProperties: { type: 'string' }, - }, - }, - }, - required: ['fields'], - }, - deck_config: { - type: 'object', - description: 'md-deck 组件配置', - properties: { - size: { - type: 'string', - description: '卡牌尺寸,格式 "宽 x 高"(单位 mm)', - default: '54x86', - }, - grid: { - type: 'string', - description: '网格布局,格式 "列 x 行"', - default: '5x8', - }, - bleed: { - type: 'number', - description: '出血边距(mm)', - default: 1, - }, - padding: { - type: 'number', - description: '内边距(mm)', - default: 2, - }, - shape: { - type: 'string', - description: '卡牌形状', - enum: ['rectangle', 'circle', 'hex', 'diamond'], - default: 'rectangle', - }, - layers: { - type: 'string', - description: '正面图层配置,格式 "字段:行,列范围,字体大小"', - }, - back_layers: { - type: 'string', - description: '背面图层配置', - }, - }, - }, - description: { - type: 'string', - description: '卡牌组的介绍描述(可选)', - }, - }, - required: ['deck_name', 'output_dir'], - }, - }, { name: 'deck_frontmatter_read', description: '读取 CSV 文件的 frontmatter(包含模板定义和 deck 配置)', diff --git a/src/cli/prompts/design-card-game.ts b/src/cli/prompts/design-card-game.ts index e9fa9e0..4694729 100644 --- a/src/cli/prompts/design-card-game.ts +++ b/src/cli/prompts/design-card-game.ts @@ -1,6 +1,6 @@ /** * design-card-game Prompt - * + * * 引导用户设计新的卡牌游戏系统,定义卡牌模板和字段结构 */ @@ -55,6 +55,48 @@ export interface ResourceContent { }; } +/** + * 数据类型 + */ +export type DataType = 'word' | 'paragraph' | 'number' | 'symbol' | 'symbol_list'; + +/** + * 字段功能 + */ +export type FieldFunction = 'identify' | 'compare' | 'flavor' | 'rule'; + +/** + * 字段定义 + */ +export interface FieldDefinition { + name: string; + description: string; + dataType: DataType; + function: FieldFunction; + examples?: string[]; +} + +/** + * 数据类型说明 + */ +const DATA_TYPE_DESCRIPTIONS: Record = { + word: '词语 - 短文本(如名称、类型、职业等)', + paragraph: '文本段落 - 长描述(如效果说明、背景故事等)', + number: '数字 - 可比较的数值(如费用、强度、等级等)', + symbol: '单一符号 - 图标/标记(如阵营符号、元素标志等)', + symbol_list: '符号列表 - 多个图标/标记(如属性加成列表、技能图标等)' +}; + +/** + * 字段功能说明 + */ +const FIELD_FUNCTION_DESCRIPTIONS: Record = { + identify: '辨识 - 用于识别卡牌身份(如名称、编号等)', + compare: '比较 - 用于游戏机制中的数值比较(如费用、攻击力等)', + flavor: '游戏风味描述 - 提供背景故事/氛围(如背景故事、引言等)', + rule: '规则确认 - 明确游戏规则相关的信息(如效果文本、触发条件等)' +}; + /** * 生成 design-card-game prompt 的消息 */ @@ -77,80 +119,150 @@ export function designCardGame(options?: DesignCardGameOptions): DesignCardGameR type: 'text', text: `好的!我来帮你设计"${deckName}"卡牌组的字段结构。 -首先,让我了解一些基本信息: - -## 1. 卡牌类型 - -这个卡牌游戏主要包含什么类型的卡牌?(可多选) -- [ ] 角色卡(Characters)- 代表游戏中的人物或生物 -- [ ] 物品卡(Items)- 装备、道具、消耗品等 -- [ ] 法术/技能卡(Spells/Abilities)- 魔法、技能、动作等 -- [ ] 地点卡(Locations)- 场景、区域、地图等 -- [ ] 事件卡(Events)- 随机事件、剧情触发等 -- [ ] 其他:_______ - -## 2. 核心机制 - -卡牌游戏的核心机制是什么? -- 是否有费用/资源系统?(如:法力值、行动点等) -- 是否有稀有度系统?(如:普通、稀有、史诗、传说) -- 是否有阵营/派系系统? -- 是否有等级/强度系统? - -## 3. 卡牌信息 - -每张卡牌通常需要展示哪些信息? -- 卡牌名称(必填) -- 卡牌描述/效果(必填) -- 插画/图片(可选) -- 其他属性:_______ - -请告诉我你的选择,我会为你生成合适的字段结构。 +我们将通过以下步骤完成设计: --- -**示例参考:** +## 步骤 1:列出卡牌属性 -如果你想要快速开始,可以参考以下常见配置: +首先,请列出你的卡牌包含哪些属性(字段)。 -### 魔法物品卡牌组 -\`\`\`json -{ - "fields": [ - {"name": "name", "description": "物品名称"}, - {"name": "type", "description": "物品类型", "examples": ["武器", "防具", "饰品", "消耗品"]}, - {"name": "rarity", "description": "稀有度", "examples": ["普通", "稀有", "史诗", "传说"]}, - {"name": "cost", "description": "价格/费用"}, - {"name": "effect", "description": "效果描述"} - ] -} +**思考方向:** +- 玩家看到这张卡牌时,需要知道什么信息? +- 游戏机制需要用到哪些数据? +- 哪些信息用于区分不同的卡牌? +- 哪些信息用于游戏过程中的判断? + +**示例:** +- 名称、费用、类型、效果、稀有度... + +--- + +## 步骤 2:定义每个属性的数据类型 + +对于每个属性,选择其数据类型: + +| 类型 | 说明 | 示例 | +|------|------|------| +| word(词语) | 短文本 | "火球术"、"战士"、"传说" | +| paragraph(文本段落) | 长描述 | "造成 5d6 点火焰伤害,范围内的所有生物进行敏捷豁免" | +| number(数字) | 可比较的数值 | 3、5、10 | +| symbol(单一符号) | 图标/标记 | 🔥、⚔️、🛡️ | +| symbol_list(符号列表) | 多个图标/标记 | 🔥🔥🔥、⚔️🛡️💫 | + +--- + +## 步骤 3:定义每个字段的功能 + +对于每个属性,说明其在游戏中的功能: + +| 功能 | 说明 | 示例 | +|------|------|------| +| identify(辨识) | 用于识别卡牌身份 | 名称、编号、唯一标识 | +| compare(比较) | 用于游戏机制中的数值比较 | 费用、攻击力、防御力 | +| flavor(游戏风味描述) | 提供背景故事/氛围 | 背景故事、引言、 lore | +| rule(规则确认) | 明确游戏规则相关的信息 | 效果文本、触发条件、限制 | + +--- + +## 步骤 4:关于插画/美术设计 + +**默认设定:** 卡牌的背景部分视为插画或精美的装帧设计,不需要在数据中定义插画字段。 + +如果你需要在卡牌上显示特定的图标或符号,请使用 symbol 或 symbol_list 类型。 + +--- + +## 回复格式 + +请按以下格式回复(可以复制模板填写): + +\`\`\` +### 属性 1:[属性名称] +- **描述**:[简短说明这个属性的用途] +- **数据类型**:[word/paragraph/number/symbol/symbol_list] +- **字段功能**:[identify/compare/flavor/rule] +- **示例值**:[2-3 个示例值] + +### 属性 2:[属性名称] +... \`\`\` -### NPC 角色卡牌组 -\`\`\`json -{ - "fields": [ - {"name": "name", "description": "角色名称"}, - {"name": "class", "description": "职业", "examples": ["战士", "法师", "盗贼", "牧师"]}, - {"name": "level", "description": "等级"}, - {"name": "background", "description": "背景故事"}, - {"name": "ability", "description": "特殊能力"} - ] -} +--- + +## 示例参考 + +### 魔法物品卡牌组 + +\`\`\` +### 属性 1:name(名称) +- **描述**:物品的名称 +- **数据类型**:word +- **字段功能**:identify +- **示例值**:火球术卷轴、治疗药水、隐形斗篷 + +### 属性 2:cost(费用) +- **描述**:购买或使用该物品所需的资源 +- **数据类型**:number +- **字段功能**:compare +- **示例值**:3、5、2 + +### 属性 3:type(类型) +- **描述**:物品的分类 +- **数据类型**:word +- **字段功能**:identify +- **示例值**:武器、防具、饰品、消耗品 + +### 属性 4:rarity(稀有度) +- **描述**:物品的稀有程度 +- **数据类型**:word +- **字段功能**:compare +- **示例值**:普通、稀有、史诗、传说 + +### 属性 5:effect(效果) +- **描述**:物品的效果或使用说明 +- **数据类型**:paragraph +- **字段功能**:rule +- **示例值**:造成 5d6 点火焰伤害、恢复 2d4+2 点生命值 \`\`\` ### 塔罗牌组 -\`\`\`json -{ - "fields": [ - {"name": "name", "description": "牌名"}, - {"name": "number", "description": "编号"}, - {"name": "element", "description": "元素", "examples": ["火", "水", "风", "土"]}, - {"name": "meaning", "description": "含义"}, - {"name": "reversed", "description": "逆位含义"} - ] -} -\`\`\`` + +\`\`\` +### 属性 1:name(牌名) +- **描述**:塔罗牌的名称 +- **数据类型**:word +- **字段功能**:identify +- **示例值**:愚者、魔术师、女祭司 + +### 属性 2:number(编号) +- **描述**:在大阿卡纳中的序号 +- **数据类型**:number +- **字段功能**:identify +- **示例值**:0、1、2 + +### 属性 3:element(元素) +- **描述**:对应的元素 +- **数据类型**:symbol +- **字段功能**:flavor +- **示例值**:🔥、💧、💨、🌍 + +### 属性 4:meaning(含义) +- **描述**:正位时的含义 +- **数据类型**:paragraph +- **字段功能**:rule +- **示例值**:新的开始、冒险、自发性 + +### 属性 5:reversed(逆位含义) +- **描述**:逆位时的含义 +- **数据类型**:paragraph +- **字段功能**:rule +- **示例值**:鲁莽、冒险导致的失败、缺乏计划 +\`\`\` + +--- + +请开始描述你的卡牌属性吧!` } } ];