Skip to content

《Legends of Furry》卡牌数据化重构任务书

文档状态:阶段 0~7 已完成,数据库内容已成为唯一权威来源
编制日期:2026-08-24
适用项目:D:\LegendsOfFurry\Project
目标版本:卡牌内容数据库化 + WPF 卡牌维护工具 MVP


1. 项目结论

本次重构应以“策划可以独立创建、修改、校验并发布卡牌”为唯一主线,不应只把 switch(cardId) 换成另一种硬编码形式。

目标形态如下:

text
策划

WPF 卡牌维护工具
  ↓ 读写
SQLite 内容数据库(唯一编辑源)
  ↓ 校验、编译、发布
Unity 只读内容包(JSON + 图片资源 + manifest)
  ↓ 加载
CardRegistry

卡牌执行管线
  ├─ Trigger(何时执行)
  ├─ Condition(满足什么条件)
  ├─ TargetSelector(作用于谁)
  ├─ ValueExpression(数值如何计算)
  └─ Effect(执行什么通用操作)

最终,新增一张由现有通用能力组合而成的卡牌时,不修改 C#、不创建 Unity ScriptableObject、不重新编译脚本。策划只需在 WPF 工具中录入、验证并发布,即可在游戏中使用。

需要明确的能力边界:

  • “造成伤害、治疗、加护甲、抽牌、施加状态、移动、击退、复活、改变资源、操作牌区”等是可复用规则能力。
  • 一张具体卡牌使用哪些能力、数值、目标、条件和触发时机属于数据库内容。
  • 策划可在不写代码的情况下组合已有规则能力。
  • 如果设计出引擎从未具备的新基础规则,例如“交换两名角色的生命百分比”,仍需程序先新增一个通用 Effect 或表达式能力;新增后的能力会自动出现在维护工具中,之后可被任意卡牌复用。
  • 数据库中不得存放可执行 C#、程序集类型名或任意脚本。只存受白名单和参数结构约束的规则节点,防止内容破坏游戏状态或导致运行时注入问题。

2. 已检查的项目现状

2.1 技术基线

  • Unity 版本:6000.5.7f1
  • 自有运行时代码约 30 个 C# 文件,核心卡牌链路约 2,000 行,整个自有脚本约 4,900 行。
  • 项目使用 Unity 生成的旧式 Assembly-CSharp.csproj,目标框架显示为 .NET Framework 4.7.1,Unity 同时启用 .NET Standard 2.1 API。
  • 本机已安装 .NET 10 SDK,并具备 WPF 桌面运行时。工具项目建议独立使用 net8.0-windows,避免改写 Unity 自动生成的解决方案文件。
  • 当前仓库存在未跟踪的演示构建目录和压缩包,本次规划不触碰这些文件。

2.2 当前卡牌来源

目前存在两套并行的数据来源:

  1. Assets/Script/Cards/Data/CardCatalog.cs

    • 运行时用 C# 创建 61 张卡牌。
    • 卡名、费用、池、稀有度、目标方式、描述等均硬编码。
    • 同文件还硬编码了装备池名称、初始牌库生成规则和稀有度概率。
  2. Assets/Cards/Data/*.asset

    • 另有 5 个旧版 CardData 资产。
    • PlayerStartingDeck.asset 使用其中 4 种,组成 10 张基础牌。
    • CD_FireBall.asset 存在,但它的 fireball_01 未进入当前 CardEffectResolver 的 ID 分支。

这两条路径已经产生字段重复、行为来源不一致和旧代码残留,重构后必须收敛为一个内容源。

2.3 当前执行链路

text
HandCardSystem.BuildStartingDeck

CardCatalog / DeckData

HandCardView.PlayCard

CardEffectResolver.CanAfford

HandCardSystem 选择单位/格子/方向

CardEffectResolver.TryPlay

ValidateTarget

扣除行动力和魔力

ResolveCard switch(cardId)

Unit / CombatantState / HandCardSystem / BoardClickController

卡牌结算并不只在 CardEffectResolver

  • HandCardSystem.EndTurnDiscardAll 按 ID 处理“碎裂、入魔、残缺”。
  • HandCardSystem.DrawOne 按 ID 处理“反噬”。
  • HandCardSystem.ResolveIdentify 硬编码鉴定、稀有度抽取和 20% 诅咒判定。
  • CardEffectResolver.ValidateTarget 对复活牌、剑、匕首、盾、弓和游侠存在特殊判断。
  • CombatantState 硬编码所有状态的叠层、转化、回合效果、负面状态集合和中文名称。
  • Unit.TakeTypedDamage 硬编码普通免疫、闪避、易损、穿甲伤害类型和复活逻辑。
  • CardEffectResolver.Damage 硬编码锋利、速攻、恍惚、心火和剑蓄力。
  • BattleFlowBattleBootstrapClassCatalog 硬编码职业资料、职业被动、单位数值和每回合抽牌数。
  • BoardClickController 硬编码寒冷对移动费用、力竭对行动力上限的影响。

2.4 已发现的重构风险和行为疑点

这些问题必须先建立基线测试或由设计确认,不能在迁移时静默“顺手修正”:

项目当前表现处理要求
旧效果处理器ICardEffectHandler 及 5 个处理器没有被当前解析器调用垂直切片完成后删除或正式纳入新执行器体系,禁止保留第三条执行路径
fireball_01有旧资产和旧处理器,但当前 ID 解析器无分支迁移前确认保留、删除或改为基础牌
“散射”描述为至多 3 名角色,实际只伤害一个目标由设计决定正确行为并建立验收用例
“照耀”描述写清除目标地块负面状态,实际清除目标单位负面状态由设计确认语义
“通灵”当前仅日志占位,不实际结算数据中明确标记为 Placeholder/Disabled,禁止伪装成已实现
费用同时存在 costText、行动力、魔力和 X 标记新模型只保存结构化费用,显示文本由工具和游戏自动生成
目标规则cross_brilliant 通过卡牌 ID 特判允许选择死亡单位改为目标规则字段 lifeState = Dead
卡牌触发抽到、回合结束留手等行为散落在手牌系统全部转为卡牌 Trigger 图
状态使用 C# 枚举且行为分散改为稳定字符串 ID + 数据定义 + 通用状态生命周期
校验只有编辑器菜单中的固定数量和固定 ID 检查改为数据库、发布器、运行时三层结构化校验

3. 重构目标、范围与非目标

3.1 必须实现

  1. SQLite 成为卡牌及其关联内容的唯一编辑源。
  2. WPF 工具可完成卡牌查询、新建、复制、编辑、启用/停用、删除前引用检查、校验和发布。
  3. 卡牌的费用、目标、触发时机、条件、效果顺序和参数均由数据库描述。
  4. Unity 启动时加载发布内容,构建只读 CardRegistry,不再调用 CardCatalog.Ensure()
  5. 卡牌执行不再使用具体卡牌 ID 的 switchif 或字符串前缀分支。
  6. 61 张运行时卡牌、当前基础牌和相关牌库全部完成迁移。
  7. 当前卡牌依赖的状态、牌区操作、资源变化、移动和伤害修正进入通用执行管线。
  8. WPF 发布前给出可定位到“卡牌—触发器—节点—字段”的中文错误。
  9. 内容发布采用临时文件 + 原子替换;发布失败时保留上一个可用版本。
  10. 开发阶段新增或修改的每一个方法都必须有方法级注释。

3.2 建议同时纳入

  • 卡牌池、稀有度权重、基础牌库、职业起始牌库配方。
  • 状态的名称、标签、最大层数、叠层方式、持续时间和触发行为。
  • 职业资料、职业被动和基础战斗参数。
  • 卡面图片导入、相对路径管理和缺图占位预览。
  • 测试牌库管理,保证新卡发布后能够立即在测试战斗中抽到。

3.3 本期不做

  • Steam Workshop、联网内容商店和 Mod 依赖解析。
  • 任意脚本编辑器或让策划书写 C#。
  • 完整的卡面排版/绘图软件。
  • 多人同时编辑、远程数据库和账号权限系统。
  • 全量战斗关卡编辑器。
  • 为所有未来机制预先实现 Effect;只覆盖当前卡牌和约定的通用扩展能力。

4. “策划只用维护工具新建可用卡牌”的验收定义

策划应能在未打开 IDE、未修改 C#、未手工编辑 JSON/SQLite、未创建 Unity 资产的情况下完成:

  1. 点击“新建卡牌”。
  2. 填写稳定 ID、名称、描述、稀有度、卡牌池、标签和卡图。
  3. 用结构化表单设置行动力/魔力费用和目标规则。
  4. 在效果树中选择触发时机,添加条件、目标选择和效果节点。
  5. 将卡牌加入一个卡池或“策划测试牌库”。
  6. 点击“验证”,修复所有阻断错误。
  7. 点击“发布到游戏”。
  8. 启动游戏后无需脚本重新编译即可看到、抽到并正确使用该卡牌。

首个端到端验收样例:

text
卡名:测试·淬毒斩
费用:1 行动力
目标:距离 1 内存活单位
效果顺序:
  1. 对目标造成 4 点普通伤害
  2. 若目标仍存活,施加 1 层中毒
归属:策划测试牌库

发布后应在测试战斗中抽到并完整结算,Git 差异中不得出现任何为该卡新增的 C# 分支。


5. 规则层与内容层边界

规则层(C#)内容层(SQLite)
什么叫伤害、伤害事件按什么顺序结算某张牌造成 4 点普通伤害
行动力、魔力如何检查和扣除某张牌消耗 1 行动力和 2 魔力
牌如何从手牌进入弃牌堆/消耗堆某张牌打出后消耗
如何按距离、视线、阵营和生死筛选目标某张牌选择距离 2 内死亡友方
Trigger、Condition、Effect 如何调度某张牌在抽到时触发 5 点暗伤
状态生命周期、叠层和到期的通用算法中毒的最大层数、回合效果和衰减量
棋盘寻路、移动合法性、击退碰撞规则某效果免费移动 2 格或击退 1 格
内容加载、版本、校验、回退卡名、卡图、池、标签、排序和启用状态
Effect 执行器的实现卡牌引用哪个 Effect 及其参数
值表达式的安全求值伤害值为 3 × 本次消耗魔力

判断准则:新增同类型卡牌是否需要改 C#。若需要,先判断缺的是不是一个可复用的规则能力;只有缺少新规则能力时才允许开发 Effect。


6. 目标工程结构

text
Project
├─ Assets
│  ├─ Script
│  │  ├─ Core
│  │  │  ├─ Combat
│  │  │  ├─ Cards
│  │  │  ├─ Statuses
│  │  │  ├─ Targeting
│  │  │  └─ Turns
│  │  ├─ Content
│  │  │  ├─ Contracts
│  │  │  ├─ Loading
│  │  │  ├─ Validation
│  │  │  └─ Registry
│  │  └─ Presentation
│  │     ├─ Cards
│  │     ├─ Board
│  │     └─ UI
│  ├─ StreamingAssets
│  │  └─ Content                 # 发布器生成,游戏只读
│  └─ Tests
│     ├─ EditMode
│     └─ PlayMode
├─ ContentSource
│  ├─ lof-content.db             # 策划唯一编辑源
│  ├─ Artwork                    # 原始卡图/导入资源
│  ├─ Backups
│  └─ Migrations
├─ Tools
│  ├─ LegendsOfFurry.Content.sln # 独立于 Unity 自动生成的 sln
│  ├─ Content.Contracts
│  ├─ Content.Persistence
│  ├─ Content.Compiler
│  ├─ CardEditor.Wpf
│  └─ Content.Tests
└─ Docs

Unity 自动生成的 Assembly-CSharp.csproj 和现有解决方案不作为手工维护目标。WPF 工具及发布器使用独立解决方案。


7. 数据存储与发布方案

7.1 方案决定

  • SQLite 是编辑态权威数据源,WPF 直接通过事务读写。
  • Unity 不直接编辑 SQLite,也不依赖桌面数据库驱动。
  • Content.Compiler 从 SQLite 读取已启用内容,执行完整校验后导出确定性的只读 JSON 内容包。
  • 导出内容放入 Assets/StreamingAssets/Content,构建后仍可按需要被外部内容包覆盖。
  • 内容包包含 manifest.json、卡牌/状态/牌库数据、资源清单和图片。
  • manifest 至少记录 schema 版本、内容版本、生成时间、记录数和 SHA-256 校验值。
  • 游戏加载失败时必须停止进入战斗并显示可读错误;开发环境可选择回退到最近一次成功内容包,但不能悄悄加载半套数据。

这一方案同时满足“可扩展内容进入数据库”和“Unity 运行时稳定、跨平台、只读、可测试”的要求。

7.2 数据库核心表

建议使用以下逻辑表;字段名最终以迁移脚本为准。

主要职责
schema_info数据库 schema 版本和迁移历史
content_versions内容版本、发布说明、创建时间和发布状态
cards卡牌基础信息、结构化费用、目标入口、卡图、启用状态
card_pools卡牌池/装备池定义
card_pool_members卡牌与池的多对多关系、权重和排序
card_tags / card_tag_members攻击、剑、诅咒、临时牌等可扩展标签
behaviors某卡牌/状态/职业在某 Trigger 下的行为图入口
behavior_nodesSequence、Condition、Effect、Repeat、ForEach 等节点
statuses状态名称、正负面标签、最大层数、叠层和持续策略
decks / deck_entries基础牌库、测试牌库及固定卡牌数量
deck_recipes按池、稀有度、权重随机生成的职业起始牌库配方
rarities稀有度显示、颜色、默认权重和排序
class_profiles职业名称、说明、初始资源和牌库配方引用
game_settings手牌上限、每回合抽牌、基础行动点等平衡配置
assets卡图、图标、VFX key 与相对路径/资源 key

7.3 cards 关键字段

  • card_id:稳定字符串主键;创建后不可随意改名。
  • display_namedescription:显示内容。
  • artwork_key:资源表引用,不保存本机绝对路径。
  • rarity_idfamily_tagenabledsort_order
  • action_costmana_costspend_all_actionspend_all_mana
  • is_attackexhaust_on_playtemporarycurseunplayable
  • selection_mode:Self、Unit、Cell、Direction、None。
  • rangeteam_filterlife_state_filterrequires_line_of_sightallow_self
  • row_versioncreated_atupdated_at

costText 不再作为权威字段,始终由结构化费用自动生成。

7.4 行为图结构

behaviors 使用稳定 Trigger key;behavior_nodes 以父子关系和顺序构成树:

text
OnPlay
└─ Sequence
   ├─ Effect: Damage
   │  ├─ target = SelectedUnit
   │  ├─ amount = Constant(5)
   │  └─ damageType = True
   └─ Condition: TargetKilledByThisCard
      └─ Then
         └─ Effect: ModifyActionPoints(+2)

节点参数以结构化 JSON 保存,但只能由注册的节点 schema 生成和校验。数据库不存 CLR 类型名,运行时只按稳定的 operation_key 查找执行器。


8. 通用规则能力清单

8.1 Trigger

首期至少支持:

  • OnPlay
  • OnDraw
  • OnAddedToHand
  • OnTurnStartInHand
  • OnTurnEndInHand
  • OnDiscard
  • OnExhaust
  • OnDamageResolved
  • OnKill
  • OnBattleStart

状态和职业行为还需支持 BeforeDealDamageBeforeTakeDamageAfterDealDamageAfterTakeDamageOnUnitTurnStartOnUnitTurnEndOnMoveCostCalculated

8.2 Condition

首期至少支持:

  • 目标是否存活/死亡、是否自己、阵营是否匹配。
  • 目标是否有护甲、状态、标签或指定层数。
  • 当前卡牌是否具有指定标签/池/稀有度。
  • 本卡是否击杀目标、伤害是否命中、生命伤害是否大于零。
  • 当前资源、生命、护甲是否满足比较式。
  • 本次是否消耗 X 行动力/魔力。
  • 概率判定。
  • 逻辑组合:All、Any、Not。

8.3 TargetSelector

首期至少支持:

  • Self、SelectedUnit、SelectedCell。
  • 选中目标周围的单位、曼哈顿范围内单位。
  • 指定方向的直线单位、面前 1×3 范围。
  • 所有友方、所有敌方、所有单位。
  • 当前卡牌实例、手牌、抽牌堆、弃牌堆、消耗堆。
  • 对结果设置数量上限、随机、排序和是否包含中心目标。

卡牌只负责描述选谁;距离、视线、地形阻挡、占用、生死和阵营检查由规则层统一执行。

8.4 ValueExpression

不得把公式作为任意字符串直接求值。使用受控表达式树:

  • Constant。
  • SpentActionPoints、SpentMana。
  • CurrentHealth、MaxHealth、Armor、StatusStacks。
  • CardRuntimeCounter。
  • Add、Subtract、Multiply、Divide、Min、Max、Floor、Clamp。

这样可表示 3 × 本次消耗魔力当前护甲 ÷ 2基础伤害 + 卡牌永久加成 等当前需求。

8.5 Effect

为覆盖现有卡牌,首期至少实现:

类别Effect
战斗Damage、Heal、GainArmor、Revive、KnockBack
状态AddStatus、SetStatus、ReduceStatus、RemoveStatus、ClearStatuses
资源ModifyActionPoints、ModifyMaxActionPoints、ModifyMana、SpendResource
卡牌DrawCards、GenerateCard、MoveCards、RemoveCardsByQuery、ModifyCardRuntimeValue
牌堆交互RevealTopCardsAndChooseDiscard、PlayTopCardsForFree
移动BeginFreeMove
流程EndTurn、NoOp/Placeholder
表现PlayVfx、PlaySfx;失败不得改变核心结算

每个执行器必须声明自己的参数 schema、是否需要目标、是否可能等待玩家输入、是否修改牌区,以及可供 WPF 显示的中文名称和字段说明。


9. 新卡牌执行管线

text
1. 读取 CardDefinition
2. Preflight
   ├─ 卡牌是否启用/可打出
   ├─ 资源是否足够
   ├─ 目标入口是否合法
   └─ 所有节点和执行器是否可用
3. 建立 CardExecutionContext
   ├─ 卡牌实例
   ├─ 施放者、已选单位/格子/方向
   ├─ 费用快照与资源快照
   └─ 本次伤害/击杀/选择结果
4. 原子提交费用与出牌状态
5. 依顺序执行 OnPlay 行为图
6. 每个节点解析 Condition、Target、Value 后调用 EffectExecutor
7. 支持协程式等待 VFX、卡牌选择或额外目标选择
8. 执行 AfterPlay、OnKill、状态和伤害事件
9. 根据定义进入弃牌堆/消耗堆
10. 记录结构化战斗日志并刷新 UI

必须解决的工程约束:

  • 费用预检和提交分开,预检失败不能扣资源。
  • 行为图执行过程中不得再次按卡牌 ID 判断。
  • 多段伤害共享同一个执行上下文,能够判断“本卡造成击杀”。
  • 用户选择型效果必须可暂停、取消和继续;战斗结束或对象销毁时必须安全取消。
  • 伤害生命周期要固定:创建请求 → 攻击方修正 → 防守方修正 → 护甲/生命结算 → 死亡 → 事后事件。
  • 表现层异步失败不能让逻辑层重复结算。
  • 所有目标集合使用确定性排序;需要随机时使用注入的战斗随机源,便于测试和回放。

10. WPF 卡牌维护工具

10.1 技术方案

  • WPF + MVVM。
  • 工具目标框架:net8.0-windows
  • SQLite 访问封装在 Content.Persistence,UI 不直接拼 SQL。
  • 数据库 schema 使用按版本顺序执行的迁移脚本。
  • ViewModel 使用 INotifyDataErrorInfo 或等价机制展示字段错误。
  • 保存、复制、删除、发布均通过命令执行,并具有未保存更改提示。
  • 业务模型、校验规则和导出 DTO 尽量与 Unity 共享纯 C# 合同,不引用 WPF 或 Unity 类型。

10.2 主界面

建议采用三栏布局:

text
┌─────────────────────────────────────────────────────────────────┐
│ 数据库  保存  验证  发布  备份  内容版本                         │
├──────────────┬──────────────────────┬───────────────────────────┤
│ 卡牌列表      │ 基础属性             │ 行为/效果树                │
│ 搜索          │ ID、名称、描述       │ OnPlay                    │
│ 池/稀有度筛选 │ 费用、标签、卡图     │  ├ Damage                 │
│ 启用状态      │ 目标、距离、视线     │  └ AddStatus              │
│ 新建/复制     │ 牌库/卡池归属        │ 节点参数面板               │
├──────────────┴──────────────────────┴───────────────────────────┤
│ 错误/警告列表:双击定位到具体字段或效果节点                      │
└─────────────────────────────────────────────────────────────────┘

10.3 MVP 功能

  • 打开/新建内容数据库,自动迁移 schema。
  • 卡牌列表搜索、筛选、排序和启用状态显示。
  • 新建、复制、编辑和安全删除卡牌。
  • 结构化费用编辑,自动生成费用显示。
  • 目标规则编辑及目标范围摘要。
  • 卡图导入:复制到受管目录、生成资源 key、预览、检查丢失。
  • Trigger/Condition/Target/Effect 树形编辑器。
  • 根据 operation schema 动态显示正确参数控件。
  • 卡池和测试牌库分配。
  • 实时字段校验、整库校验和引用检查。
  • 一键备份数据库。
  • 一键发布,并显示导出文件、版本、记录数和错误。
  • 发布日志和最近一次成功版本回退。

10.4 校验分级

  • Error:阻止保存或发布,例如重复 ID、未知 Effect、缺少必填参数、无效引用、循环行为图。
  • Warning:允许发布但需确认,例如无卡图、无牌库归属、描述与结构化数值可能不一致、占位效果。
  • Info:提示自动生成的费用文本、目标摘要和最终效果摘要。

删除被引用的卡、池、状态或资源时,工具必须列出引用方并阻止直接删除;允许先停用或完成引用迁移。


11. 现有代码迁移表

当前文件/区域迁移目标完成标准
CardCatalog.csSQLite + CardRegistry61 张卡不再由 C# 创建,删除固定数量判断
CardData.csCardDefinition DTO + 运行时 CardInstance去除 ScriptableObject 权威数据和重复费用字段
DeckData.cs / .assetdecksdeck_entries基础牌库只由发布内容构建
ClassCatalog.csclass_profilesdeck_recipes职业资料和起始牌库不再硬编码
CardEffectResolver.ResolveCard行为图执行器删除全部具体卡牌 ID 分支
ValidateTargetTargetRule + 统一目标服务复活、武器类别、职业距离均由数据和修正规则描述
HandCardSystem 的诅咒 ID 判断OnDraw / OnTurnEndInHand手牌系统不认识“碎裂”等具体卡牌
ResolveIdentifyCardQuery + GenerateCard池、稀有度、概率、诅咒均由行为图配置
CombatantState数据化状态定义 + 通用状态容器状态使用字符串 ID,行为进入 Trigger/Modifier
Unit.TakeTypedDamageDamagePipeline状态修正通过事件/修正规则参与,不按状态枚举判断
BattleFlow 职业特判职业行为图回合流程不认识战士、法师、刺客
BattleBootstrap 数值class/unit/game settings生命、魔力、抽牌数等从内容配置读取
BoardClickController 状态特判属性修正服务移动费用和最大行动点读取聚合修正结果
ICardEffectHandler 系列IEffectExecutor 或删除工程中只保留一套正式效果系统
LegendsOfFurryProjectSetup.ValidateProject内容编译器 + 自动测试不再检查固定卡牌数量和固定 ID

展示层 HandCardView 只读取 CardDefinition 和图片服务,不参与结算。


12. 实施阶段与任务拆分

状态说明:✅ 已完成🟡 已实现待 Unity/设计验收⬜ 未完成。完成标记只在对应自动化测试或阶段验收通过后更新。

截至 2026-08-24:桌面内容工具解决方案构建为 0 警告/0 错误,12 项 .NET 自动化测试全部通过;Unity 工程交互式编译成功,最近一次 EditMode 测试 14/14 通过。新增的注册表与启动预检测试待下一次 Unity 刷新后回归。交互式编辑器曾在 Unity 原生 TextCore 字体引擎中崩溃一次,确认与 C# 卡牌代码无关。

阶段 0:冻结行为基线(2~3 个有效开发日)

编号状态任务交付物/完成条件
P0-01整理当前卡牌清单已生成并校验 61 张目录卡 + 5 个旧资产,共 66 条字段快照
P0-02建立行为争议表散射、照耀、通灵、旧火球等差异已登记;阶段 5 迁移默认保持当前基线,设计变更须单独留痕
P0-03建立最小测试骨架已具备 EditMode、进入/退出 PlayMode 冒烟和独立 .NET 管线测试入口
P0-04为当前关键行为写特征测试已覆盖目录、费用、牌库、职业、状态、图解释、条件/目标/表达式、效果、伤害、异步和日志
P0-05制定分支和数据备份规则已完成单一权威源、备份时点、二进制数据库分支合并、发布回退和恢复演练规范

阶段门:现有行为被测试固定,所有已知不一致均有明确处理结论。

阶段 1:共享合同、SQLite 与发布器(4~6 日)

编号状态任务交付物/完成条件
P1-01建立独立工具解决方案Contracts、Persistence、Compiler、WPF、Tests 均可构建
P1-02定义稳定 DTO 和 key 规范schema v1 及 Trigger、Effect、Condition、Target、Expression 白名单已建立
P1-03实现 SQLite schema v1表、外键、索引、约束、种子数据和迁移记录已实现并测试
P1-04实现 Repository/Unit of Work聚合 CRUD 使用参数化 SQL 和事务,删除引用受外键保护
P1-05实现 ContentValidator已覆盖 ID/引用、操作 key、参数 JSON、表达式、图结构和资源
P1-06实现 ContentCompiler已实现确定性 JSON、manifest、SHA-256、版本目录和原子 current 指针
P1-07建立 schema/导出测试已验证 schema、完整聚合往返和重复导出内容/校验值一致

阶段门:✅ 已通过 seed-sample 命令及自动化测试验证可从空数据库创建样例伤害卡;发布器测试验证可生成有效内容包。

阶段 2:Unity 内容加载垂直切片(4~6 日)

编号状态任务交付物/完成条件
P2-01实现 ContentPackageLoaderUnity 测试已验证加载、版本、路径和 SHA-256 检查
P2-02实现 CardRegistryUnity 测试已验证卡牌、牌库和启用内容只读索引
P2-03改造 CardDefinition/CardInstance新定义、兼容视图、测试牌库展开均已接入并通过 Unity 测试
P2-04实现基础执行器Damage、Heal、GainArmor、DrawCards、BeginFreeMove、EndTurn 已通过 Unity 组合回归
P2-05打通费用和基础目标固定/X/免费费用及 Self/Unit/Cell/Direction、阵营、存活、距离、视线已通过 Unity 回归
P2-06打通最小 WPF 录入页自动化测试已完成新建伤害牌→SQLite→测试牌库→发布包链路

阶段门:首张完全由 WPF → SQLite → 内容包 → Unity 的卡牌可在战斗中使用。

阶段 3:完整卡牌规则引擎(8~12 日)

编号状态任务交付物/完成条件
P3-01建立 CardExecutionContext资源快照、目标、逐次伤害/击杀记录和卡牌运行时值已通过 Unity 回归
P3-02实现行为图解释器Sequence、Condition、Effect、Repeat、ForEach、稳定排序、深度/次数保护和取消流程已通过 Unity 回归
P3-03实现 Condition/Target/Expression 注册表三类白名单注册表、受控递归表达式、确定性目标集合、正式行为图接入和启动预检已纳入 Unity 21/21 回归
P3-04完成 Effect 执行器第 8.5 节全部首期操作已进入白名单执行器并通过编译/发布预检
P3-05实现异步选择节点看牌弃置、免费打牌和免费移动均通过完成回调暂停/恢复;额外目标由现有目标确认与 foreach 续接
P3-06重构伤害生命周期DamageRequest/Resolution、前后事件、属性、护甲、免疫、复活和击杀顺序可测试
P3-07实现结构化战斗日志日志包含卡牌、Trigger、Behavior、节点、操作、目标、输入 JSON 和结果

阶段门:不增加具体卡牌分支即可表达当前全部卡牌行为。

阶段 4:状态、职业与平衡配置数据化(6~9 日)

编号状态任务交付物/完成条件
P4-01重构状态容器RuntimeStatusInstance 使用字符串 ID、层数、持续时间、来源和实例值,保留枚举兼容入口
P4-02实现状态生命周期叠层策略、数据库上限、到期、正负面分类和清除规则已接入
P4-03状态参与战斗事件状态行为运行时已接入单位回合事件;中毒、再生、腐化定义已写入数据库
P4-04数据化职业资料和被动五职业资料和被动从内容包加载,BattleFlow 已移除职业枚举行为分支
P4-05数据化牌库配方和稀有度权重新牌库构建入口只读取职业配方、卡池查询和稀有度权重;旧内容保留到阶段 5 迁移
P4-06数据化基础战斗参数手牌上限、起手/每回合抽牌、基础行动点、职业生命和魔力均有数据库配置来源

阶段门:卡牌相关的可扩展内容不再散落在 Unit、BattleFlow、HandCardSystem 和输入控制器中。

阶段 0~4 验收记录(2026-08-24):独立 .NET 测试 12/12;隔离 Unity EditMode/PlayMode 组合回归 21/21;主程序集与编辑器测试程序集均为 0 编译错误;phase4 内容包发布成功,包含 1 张管线样例卡、20 个状态、5 个职业资料、1 套测试牌库和基础战斗参数。

阶段 5:全量迁移与等价性验证(6~9 日)

编号任务交付物/完成条件
P5-01✅ 编写一次性迁移工具migrate-legacy 从 Unity 导出 CSV 自动生成数据库,不人工复制字段
P5-02✅ 迁移基础牌、61 张卡、池和牌库66 张基线记录、全部卡池、基础牌库和全卡测试牌库已入库
P5-03✅ 迁移所有状态和职业行为20 状态、5 职业、职业牌库配方和卡牌生命周期触发器已数据化
P5-04✅ 逐卡数据校验66/66 通过 schema、引用、行为图和 Unity 运行时能力预检
P5-05✅ 代表性逐类 PlayMode 测试Trigger、Condition、Target、Expression、Effect 与伤害生命周期均有回归
P5-06✅ 全量冒烟66 张进入测试牌库;可打出卡均有 on_play,诅咒生命周期入口已验证

阶段门:正式内容完全来自数据库发布包,旧目录只作为迁移历史存在。

阶段 6:WPF 工具完善(8~12 日)

编号任务交付物/完成条件
P6-01✅ 完成主界面和导航列表搜索、三页编辑区、定位错误面板和未保存提示已完成
P6-02✅ 完成卡牌基础编辑字段、费用、目标、卡图上传/预览/备份、池、标签和测试牌库入口已完成
P6-03✅ Electron 全量迁移Vue 3 + Element Plus + Electron + SQLite;功能、校验、发布、回退与卡面拖拽已迁移,WPF 暂留回退
P6-03✅ 完成效果树编辑器简易效果与无损高级树;增删、复制、排序、缩进、分支、JSON 参数已完成
P6-04✅ 完成删除/引用保护外键阻止悬空引用,界面提示先解除引用或停用
P6-05✅ 完成备份、版本和发布体验保存/删除/发布自动备份,一键验证发布,历史版本原子回退
P6-06✅ 完成策划使用说明新建到发布流程、常见错误、能力词典和阶段 5 迁移报告已交付
P6-07✅ 策划可用性验收包独立验收样例已写入说明;WPF 发布版启动、保存链路和发布级校验通过

阶段门:达到第 4 节的最终验收定义。

阶段 5~6 验收记录(2026-08-24):正式 phase5 包包含 66 张卡、20 个状态、5 个职业资料、2 套固定牌库;内容校验 0 error;.NET 测试 12/12;隔离 Unity 回归 23/23;WPF Release 与 win-x64 发布版编译成功。缺少的 61 张卡图保持可见 warning,不阻止策划测试。

阶段 7:切换、清理与发布(4~6 日)

编号任务交付物/完成条件
P7-01 ✅删除旧执行路径已移除 ResolveCard switch、旧 Handler、运行时 CardCatalog 和 ID 特判
P7-02 ✅删除旧权威资产依赖场景和牌库已解除旧 CardData/DeckData 资产引用,旧资产已物理删除
P7-03 ✅构建前内容检查ContentBuildValidator 在构建前验证 current、SHA-256、合同和执行能力
P7-04 ✅完成回归与构建验证Unity 21/21、.NET 13/13、WPF Release 和 Windows Player 构建通过
P7-05 ✅更新开发文档已补充阶段 7 架构、Effect 扩展、注释规范和故障处理报告

阶段门:工程中不存在按具体卡牌 ID 执行业务逻辑的生产代码。

阶段 7 验收记录(2026-08-24):生产代码静态扫描不存在具体卡牌 ID 执行分支、CardCatalogResolveCardDeckData 或旧 Handler;5 张旧 CardData 资产和 PlayerStartingDeck.asset 已删除;隔离 Unity 编辑器测试 21/21,.NET 测试 13/13,Windows x64 Player 构建成功。


13. 测试策略

13.1 内容层测试

  • SQLite 迁移可从空库升级到最新版本。
  • 外键、唯一 ID、停用引用、资源路径和行为图结构校验。
  • 每个 operation key 的参数 schema 正反例。
  • 导出稳定性、manifest 校验值和损坏内容检测。
  • 数据库自动备份和发布失败回退。

13.2 规则层单元测试

  • 固定费用、X 行动力、X 魔力和免费打牌。
  • 目标距离、视线、阵营、生死、方向和范围扩展。
  • 伤害类型、护甲、免疫、闪避、多段、增减伤和击杀。
  • 状态叠层、上限、持续时间、清除、转化和回合触发。
  • 抽牌、弃牌、消耗、洗牌、生成卡、移除卡族和手牌上限。
  • 确定随机种子下的稀有度和 CardQuery 结果。

13.3 集成测试

  • WPF 新建卡 → 保存数据库 → 发布 → Unity 加载 → 加入测试牌库 → 打出。
  • 代表性复杂卡:连斩、刺击、蓄力狙杀、招雷术、魔力风暴、封喉、反噬、碎裂、鉴定、预演、占卜、复活。
  • 内容包版本不兼容、未知 Effect、缺失状态、缺失图片、重复 ID 时的错误体验。
  • 场景重载、战斗结束和异步选择中断不造成重复扣费或重复结算。

13.4 回归红线

  • 不允许以“测试不好写”为由保留具体卡牌 ID 分支。
  • 不允许只测试数据能加载,而不测试效果真实结算。
  • 不允许 WPF 校验通过但 Unity 启动后才发现未知节点。
  • WPF 与 Unity 必须复用同一套内容合同和校验规则,或通过合同一致性测试保证等价。

14. 方法注释与开发规范

用户要求“开发阶段给每个方法写上注释”,本项目将其作为 Definition of Done,而不是建议。

14.1 强制要求

  • 所有新增或被修改的方法均写 XML 文档注释,包括 publicinternalprotectedprivate、构造方法、Unity 生命周期方法、命令处理方法和测试辅助方法。
  • 注释必须说明方法目的;存在副作用、事务边界、事件顺序、线程要求、取消行为或失败策略时必须写明。
  • 有参数或返回值且含义不直观时,使用 <param><returns>;可能主动抛出的业务异常使用 <exception>
  • AwakeStartUpdate 等不能只写“Unity 生命周期函数”,应说明在本组件中完成什么初始化或调度。
  • Effect、Condition、TargetSelector 和 ValueResolver 必须在注释中写清输入、输出、支持的目标和边界情况。
  • 不接受与代码逐字重复的无信息注释,例如“设置 value 的值”。

示例:

csharp
/// <summary>
/// 验证卡牌、资源和主目标,并在全部检查通过后创建一次不可变的执行快照。
/// 此方法只做预检,不扣除行动力或魔力。
/// </summary>
/// <param name="request">包含卡牌实例、施放者和玩家选择结果的出牌请求。</param>
/// <returns>成功时返回执行快照;失败时返回可展示给玩家的错误。</returns>
public CardPreflightResult Preflight(CardPlayRequest request)

14.2 检查方式

  • 代码评审清单加入“本次新增/修改方法是否全部有有效 XML 注释”。
  • 工具项目启用 XML 文档生成和文档警告。
  • 增加一个仓库检查脚本或 Roslyn 分析器,扫描自有代码中的无注释方法;Unity 自动生成代码和第三方包排除。
  • 每个阶段门执行注释检查;不满足时任务不得标记完成。

15. 交付物

  1. ContentSource/lof-content.db 及版本化迁移脚本。
  2. WPF 卡牌维护软件及可直接启动的 Windows 发布包。
  3. Content.Contracts、Persistence、Validator、Compiler 及自动测试。
  4. Unity 内容加载、注册表、卡牌执行管线、状态和伤害生命周期。
  5. 迁移后的全部正式卡牌、状态、卡池、牌库、职业和基础配置。
  6. 生成的 Unity 只读内容包与 manifest。
  7. 策划使用说明、Effect/Condition/Target 能力词典。
  8. 程序扩展新通用 Effect 的开发说明。
  9. 自动化测试和内容构建前校验。
  10. 旧硬编码清理报告和迁移差异报告。

16. 工期评估与里程碑

以一名熟悉 Unity/C#/WPF 的开发者全职计算:

里程碑内容预计有效开发日
M1行为基线、数据库、合同、发布器6~9
M2Unity 垂直切片,WPF 可创建第一张卡4~6
M3完整卡牌规则引擎8~12
M4状态、职业、牌库和配置数据化6~9
M561 张卡及旧资产全量迁移6~9
M6WPF 完整 MVP 和策划验收8~12
M7清理、回归、构建和文档4~6
合计达到本任务书全部目标42~63

如果只做“卡牌基础字段 + 常用 OnPlay 效果 + 简易 WPF”,可在约 20~30 日形成受限 MVP;但它无法完整覆盖当前诅咒、状态、职业被动、看牌选择和复杂卡牌,不应宣称已达到最终目标。

建议按阶段门交付,优先在 M2 证明完整链路,避免先迁移 61 张卡后才发现数据模型或编辑器不适用。


17. 主要风险与控制措施

风险后果控制措施
把具体卡牌改写成 61 个“自定义 Effect”表面数据化,实际仍需程序维护Effect 命名必须是通用动词,评审禁止卡牌专属执行器
JSON 参数过于自由WPF 与 Unity 对字段理解不一致operation schema 唯一来源,编辑、校验、导出和运行时共同使用
状态仍用 enum 特判新卡引用新状态仍需改多处 C#稳定字符串 ID、状态定义和事件修正器
异步选择/动画与逻辑混杂重复扣费、卡死或结算两次执行上下文、显式暂停状态、取消令牌和逻辑/表现分离
数据库损坏或发布半成品游戏无法启动或卡牌缺失事务、自动备份、临时目录、原子替换、manifest 校验
迁移时顺手改变旧行为难以定位回归先写特征测试,争议行为由设计签字确认
新卡创建后游戏中找不到未加入任何牌库/池工具提供测试牌库,发布前对“无入口卡牌”给出警告
卡图使用绝对路径换电脑或构建后丢失工具导入并复制到受管资源目录,只存相对 key
Unity 与工具框架不兼容共享程序集无法加载共享合同保持纯 C#/.NET Standard 兼容,WPF/Unity 适配层分离
一次性大爆炸替换长期无法得到可运行版本垂直切片、双读仅限短期迁移、阶段门后立即删除旧路径

18. 最终验收清单


19. 开发启动顺序

确认本任务书后,开发不得直接从 WPF 界面或 61 张卡迁移开始。首批任务固定为:

  1. 确认第 2.4 节的行为疑点。
  2. 建立当前版本的特征测试和卡牌清单快照。
  3. 定义共享合同、稳定 key 和 schema v1。
  4. 实现 SQLite → 校验 → 只读内容包的发布链。
  5. 用一张基础伤害牌完成 WPF 到 Unity 的垂直切片。
  6. 垂直切片评审通过后再扩充规则引擎并迁移全部内容。

任何阶段如果需要新增专属于某张卡的 C# 分支,应暂停实现并回到规则能力评审,确认它是否能够抽象成可复用 Effect、Condition、TargetSelector 或 ValueExpression。