From 3f0265e40f85c14ca65a176463d0ca2cf2ff7187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A1=83=E6=A0=91=E5=A4=AD?= Date: Wed, 16 Sep 2026 15:57:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=9F=E6=B3=95=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/combat-v6-manual-first-release-plan.md | 328 + docs/combat-v6-manual-system-design.md | 35 +- docs/combat-v6-polish-and-closure-plan.md | 2 +- drizzle/0049_manual_first_release.sql | 39 + drizzle/meta/0049_snapshot.json | 8420 +++++++++++++++++ drizzle/meta/_journal.json | 7 + .../feature/items/presentation/basic.ts | 14 +- .../feature/manuals/ManualJadePicker.tsx | 10 +- .../feature/manuals/ManualRealmSlot.tsx | 4 +- .../components/feature/manuals/ManualRoom.tsx | 44 +- .../combat-v6/CombatV6ManualService.ts | 7 +- src/shared/combat-v6/public-build.test.ts | 6 +- .../engine/combat-v6/content/jiujie-pack.ts | 2 +- .../combat-v6/content/skill-learning.test.ts | 4 +- src/shared/engine/combat-v6/core/damage.ts | 3 + src/shared/engine/combat-v6/core/effects.ts | 4 +- src/shared/engine/combat-v6/core/hooks.ts | 1 + src/shared/engine/combat-v6/core/passives.ts | 1 + src/shared/engine/combat-v6/core/types.ts | 10 + src/shared/engine/combat-v6/core/when.ts | 13 + .../engine/combat-v6/manuals/attributes.ts | 9 + .../engine/combat-v6/manuals/compiler.ts | 19 +- .../combat-v6/manuals/data/manual-pack.json | 820 +- .../manuals/data/manual-pack.schema.json | 319 +- .../combat-v6/manuals/learning-limit.test.ts | 21 +- .../engine/combat-v6/manuals/manuals.test.ts | 45 +- .../combat-v6/manuals/mechanism.test.ts | 435 + .../engine/combat-v6/manuals/mechanism.ts | 166 + src/shared/engine/combat-v6/manuals/pack.ts | 80 +- .../engine/combat-v6/manuals/presentation.ts | 63 + src/shared/engine/combat-v6/manuals/state.ts | 2 +- src/shared/engine/combat-v6/manuals/types.ts | 4 +- .../projection/compose-character-manuals.ts | 4 +- .../projection/fixtures/before-a1.json | 4 +- src/shared/manuals/action.test.ts | 129 +- src/shared/manuals/action.ts | 23 +- src/shared/rewards/data/README.md | 4 +- src/shared/rewards/data/wild.json | 200 +- src/shared/rewards/data/wild.schema.json | 13 - src/shared/rewards/wild-pack.ts | 5 +- 40 files changed, 10815 insertions(+), 504 deletions(-) create mode 100644 docs/combat-v6-manual-first-release-plan.md create mode 100644 drizzle/0049_manual_first_release.sql create mode 100644 drizzle/meta/0049_snapshot.json create mode 100644 src/shared/engine/combat-v6/manuals/attributes.ts create mode 100644 src/shared/engine/combat-v6/manuals/mechanism.test.ts create mode 100644 src/shared/engine/combat-v6/manuals/mechanism.ts create mode 100644 src/shared/engine/combat-v6/manuals/presentation.ts diff --git a/docs/combat-v6-manual-first-release-plan.md b/docs/combat-v6-manual-first-release-plan.md new file mode 100644 index 00000000..9ff97ecd --- /dev/null +++ b/docs/combat-v6-manual-first-release-plan.md @@ -0,0 +1,328 @@ +# combat-v6 首版功法改造计划 + +> 2026-09-16:根据用户确认的 24 本设计编制。状态:首版代码已实施、本地迁移已执行;表中数值是首轮验证基线,不代表已通过战斗平衡验收。 +> 现行规则见 [境界功法系统](combat-v6-manual-system-design.md)。下文保留实施分解,第 8、9 节记录最终决定和本次证据。 + +## 1. 目标与范围 + +将现有 16 本纯六维功法替换为 24 本有明确意象与构筑取舍的功法。每本由一项随层数增加的六维贡献,以及一个随层数增强的简单机制组成。 + +确认保留: + +- 炼气、筑基、金丹、元婴各一个激活位,更高境界不新增位置;同位只激活一本。 +- 九层培养;三、六层使用同名玉简解锁后续层数;每本独立培养,战外免费切换,受既有战斗与结算占用保护。 +- 学习一本玉简,三层突破两本、六层突破三本(合计六本);修炼成本采用本文末尾已确认的升级消耗修订,不引入品质倍率。 +- 六维只加入战斗/展示投影,不写入永久属性。升层与切换不得免费补满气血、法力。 +- 取消功法 common/rare 稀有度。供给由产出概率、权重、渠道等配置决定,不增加战斗预算。 + +本轮不做:连续行动、连招计数、目标记忆、失败补偿、解封反应、治疗转盾、DOT 跳数强化、内部冷却、额外行动、功法主动技能、五行伤害体系、灵根门槛、战外修为倍率、化神以上新功法位、多套构筑存档。 + +按用户“按照推荐,开始实施”的授权,每槽最多收藏六本,仍只激活一本,无遗忘或退款功能。 + +## 2. 内容与成长基线 + +### 2.1 来源与命名 + +灵感资料为用户提供的《凡人修仙传_功法境界表.xlsx》,工作表「功法表」A2:C54;其中包含五行基础功法泛称及同一功法的分段条目。只取名称、描述和意象,以下数值与效果均为本项目游戏改编,不宣称原著存在这些机制。 + +- 炼气取长春功,另自拟金、木、水、火、土五本入门功法。 +- 筑基来源重点为 A4:C11;金丹来源重点为 A20:C30;元婴来源重点为 A32:C38。 +- 使用项目正式六维名:体魄 vitality、力道 strength、灵力 spirit、根骨 endurance、身法 speed、神识 willpower。 +- 游戏境界统一使用「金丹」,不引入资料中的「结丹」作为第二个境界键。 + +### 2.2 成长公式 + +| 归属境界 | 一层加成 | 后续每层增加 | 九层加成 | +| --- | ---: | ---: | ---: | +| 炼气 | 12 | 2 | 28 | +| 筑基 | 18 | 2 | 34 | +| 金丹 | 24 | 4 | 56 | +| 元婴 | 36 | 4 | 68 | + +四槽九层合计 186 点六维。每本仅一项属性,不为难获取功法增加第二项完整属性。 + +机制一层即开放,每层线性成长:`value(L) = valueAt1 + (valueAt9 - valueAt1) × (L - 1) / 8`。条件阈值固定,突破仅解锁升级上限,不新增第二个机制。 + +配置存数值,比例以 0~1 表示;概率修正与伤害倍率使用不同 effect 类型,避免把「降低 3 个百分点」误写成「乘以 0.97」。展示不反向参与计算。金额/气血/法力只在既有最终结算处取整,不在每层插值时提前截断。概率与面板尺度转换保留所需精度,不能沿用整数面板处理使部分等级升级无效。 + +### 2.3 炼气六本 + +| 功法 | 意象 | 六维 | 机制 | 一/五/九层 | +| --- | --- | --- | --- | --- | +| 长春功 | 生机绵长,温养自身 | 体魄 | 回合末自身气血低于 50%,恢复最大气血的一定比例 | 0.25% / 0.5% / 0.75% | +| 庚金诀(自拟) | 金气锐利,先破敌锋 | 力道 | 对气血高于 70% 的目标,主动直接物理伤害提高 | 1% / 2% / 3% | +| 青木养元诀(自拟) | 木气滋生,扶伤续命 | 神识 | 主动治疗气血低于 50% 的主目标,治疗量提高 | 3% / 6% / 9% | +| 清泉诀(自拟) | 涓流回补,法力不竭 | 灵力 | 回合末自身法力低于 50%,恢复最大法力的一定比例 | 0.25% / 0.5% / 0.75% | +| 赤炎诀(自拟) | 精气充盈,火势方盛 | 灵力 | 自身气血高于 70%,主动直接法术伤害提高 | 1% / 2% / 3% | +| 厚土诀(自拟) | 厚土承压,守势稳固 | 根骨 | 自身处于防御状态,受到的直接物理伤害额外降低 | 3% / 6% / 9% | + +### 2.4 筑基六本 + +| 功法 | 意象 | 六维 | 机制 | 一/五/九层 | +| --- | --- | --- | --- | --- | +| 归元功 | 护体灵甲,稳健防御 | 根骨 | 自身拥有有效护盾,受到的直接物理、法术伤害降低 | 2% / 4% / 6% | +| 幻灵诀 | 幻影扰目,难辨真身 | 身法 | 受到直接物理攻击,该次攻击命中率降低 | 1 / 2 / 3 个百分点 | +| 青元剑诀 | 真元养剑,气盛剑锐 | 力道 | 自身气血高于 70%,主动直接物理伤害提高 | 2% / 3% / 4% | +| 大衍诀 | 壮大神识,分神守心 | 神识 | 受到封印判定,对方封印成功率降低 | 1 / 2 / 3 个百分点 | +| 三转重元功 | 真元压缩,法力精纯 | 灵力 | 自身法力高于 70%,主动直接法术伤害提高 | 2% / 3% / 4% | +| 煞妖诀 | 血煞催身,险中争胜 | 体魄 | 自身气血低于 35%,主动直接伤害提高 | 2% / 3% / 4% | + +### 2.5 金丹六本 + +| 功法 | 意象 | 六维 | 机制 | 一/五/九层 | +| --- | --- | --- | --- | --- | +| 太白剑诀 | 剑气破障,锋芒穿护 | 力道 | 对拥有有效护盾的目标,主动直接物理伤害提高 | 3% / 4.5% / 6% | +| 泰阳诀 | 精纯火力,盛阳外放 | 灵力 | 自身气血高于 70%,主动直接法术伤害提高 | 3% / 4.5% / 6% | +| 玄阴大法 | 阴魂蚀体,阴气乘隙 | 灵力 | 对带 DOT 状态的目标,主动直接法术、普通固定伤害提高 | 3% / 4.5% / 6% | +| 玉丹功 | 玉润丹养,扶危固本 | 神识 | 主动治疗气血低于 50% 的主目标,治疗量提高 | 4% / 8% / 12% | +| 天罗真功 | 儒门正气,护佑危局 | 神识 | 为气血低于 50% 的主目标施加护盾,护盾量提高 | 5% / 10% / 15% | +| 六极真魔功 | 真魔法体,受压显威 | 力道 | 自身气血低于 35%,主动直接物理、法术伤害提高 | 3% / 4.5% / 6% | + +### 2.6 元婴六本 + +| 功法 | 意象 | 六维 | 机制 | 一/五/九层 | +| --- | --- | --- | --- | --- | +| 托天魔功 | 肉身强横,重压不折 | 根骨 | 自身气血低于 50%,受到的直接物理伤害降低 | 3% / 5% / 7% | +| 明王诀 | 法体坚固,受制不溃 | 体魄 | 自身处于可解除封印,受到的直接物理、法术伤害降低 | 4% / 7% / 10% | +| 元磁神光 | 元磁牵制,护持周身 | 灵力 | 自身法力高于 50%,受到的直接法术伤害降低 | 3% / 5% / 7% | +| 浩然正气诀 | 邪扰不侵,正气护神 | 神识 | 自身带 Debuff 减益,受到的直接法术伤害降低 | 4% / 7% / 10% | +| 千浪诀 | 身随浪走,危中脱身 | 身法 | 自身气血低于 50%,直接物理攻击者该次命中率降低 | 2 / 4 / 6 个百分点 | +| 幽杀诀 | 杀意逼迫,追索残敌 | 力道 | 对气血低于 35% 的目标,主动直接伤害提高 | 3% / 4.5% / 6% | + +素女轮回功、真灵变化、采补与战外修炼效率类暂不纳入;高境界功法保留后续内容空间,不拆成同名升级书。 + +## 3. 战斗结算合同 + +这些口径需先写成共享领域测试,再接入全部内容。 + +### 3.1 适用对象与效果来源 + +- 功法只装配到人物,不给灵兽继承属性或被动。 +- 输出增幅作用于人物普攻、宗门主动技能的主目标;按物理、法术、普通固定伤害分别编译。通用伤害钩子默认跳过 Fixed,不能遗漏对普通固伤的显式分支。 +- 不强化器诀、物品、反击、连击式派生追击、HookDerived 伤害、DOT 或宗门派生追伤。宗门原技能明确声明的多段直接攻击属于原行动,不因段数新增触发计数。 +- 普通固定伤害可以强化;按目标气血比例扣血、资源扣除等特殊效果不得强化。实施前盘点效果级来源,不通过解析公式字符串、技能中文名或在 core 中硬编码宗门/功法 ID 判断。 +- 仅 skillId 白名单可能无法区分同一混合技能的不同效果。优先利用已有类型与 origin;不足时由内容编译层声明最小的通用效果分类,并由伤害路径透传,避免误伤其他系统原有修正。 +- 治疗与护盾增幅只作用于宗门主动技能的主目标。排除器诀、物品、被动恢复和功法回合恢复,不因群体目标数量获得多倍功法增益。 +- 受击减伤/闪避是佩戴者的防御能力,可应对敌方人物、灵兽、NPC 的相应直接攻击,包括敌方直接攻击器诀;与己方输出不强化器诀的规则分开。 + +### 3.2 条件与时机 + +- 「低于」「高于」严格使用 `<` 与 `>`,恰好 35%、50%、70% 均不满足对应严格条件。 +- 比例气血以当前气血 / 最大气血判断,不以伤势后的可恢复上限替换分母;法力同理。条件在每一段打击或治疗/护盾计算前读取当前事实。 +- 多段攻击跨阈值后,后续段按新血量判断;不添加行动开始快照。 +- 有效护盾指当前尚有正数吸收量的护盾。归元与太白在吸收前判断,最后一层盾被打破后后续段不再获得护盾条件收益。 +- 玄阴读取目标当前的 Dot 类状态,任意施加者均可;不追踪归属、不延长持续、不改 DOT 本身。同一行动先伤害后挂 DOT 的首段不能倒用新状态。 +- 浩然仅读取 Debuff 类;Dot、Control 不混为一类。 +- 明王读取可解除的封印控制。复用并核对既有解控类别口径,排除宗门休息/反噬、不可驱散状态和禁复活状态;不能简单等同于「所有 Control」。 +- 大衍使用现有封印公式尺度和上下限,不绕过免疫,不给必中状态额外掷骰。幻灵/千浪沿用命中钩子和下限,不把必中攻击改成可以闪避。 +- 保护发生后,防守功法读取实际承伤者;输出仍服从既有主目标身份语义,增加保护重定向专项测试。 + +### 3.3 恢复、叠加与显示 + +- 长春/清泉只在回合末对在场存活人物生效,沿用现有回合结束、DOT、战斗结束顺序,不为回血改变胜负裁定。 +- 恢复基数为相应最大资源,最终恢复受当前缺口约束。长春遵守既有禁止恢复、减疗和伤势上限,不复活,不清伤势;恢复不进入主动治疗强化链。 +- 恢复向下取整,允许低等级小基数时为 0,不私自增加保底 1。展示应准确说明比例;验证时检查低境界实际体感。 +- 伤害、治疗等倍率沿用既有计算链,不新增「功法统一加算并封顶」系统。概率差额按现有公式聚合。 +- 四槽机制输出最大倍率粗估为 `1.03 × 1.04 × 1.06 × 1.06 = 1.20360032`。这不是最终战力上限:六维、命中、抗封、恢复、护盾和其他系统交互必须另计。 +- 不增加功法专属通用触发平台。利用现有战报表达回血/回蓝及最终伤害;条件与本层效果在功法详情可见。额外逐击触发提示不作为首版必做项。 + +## 4. 当前代码事实与最小实现边界 + +| 区域 | 当前事实 | 计划变更 | +| --- | --- | --- | +| `manuals/pack.ts`、`types.ts` | effects 仅支持 1~2 项六维,rarity 为必填 | 改为一项属性成长+一个类型化机制;删除功法 rarity;更新 Zod 和生成的 JSON Schema | +| `manuals/content.ts` | 为每本生成空 effects 的被动 | 编译真实机制,不把全部机制开放为策划任意脚本 | +| `manuals/compiler.ts` | 累加属性,携带被动与 skillLevels | 按层数求机制值,输出 hooks/必要的面板贡献,避免重复应用属性 | +| `projection/compose-character-manuals.ts` | 支持追加技能/面板;面板 add 当前统一 floor | 保留投影顺序;概率尺度贡献必须保留精度;面板与战斗输出一致 | +| `core/when.ts`、`types.ts` | 已有血量、主目标、来源、类型、防御、目标状态条件 | 按需补法力比例、自身/目标护盾、自身状态筛选;只做本清单需要的字段 | +| `core/passives.ts`、`hooks.ts` | 支持修改伤害、治疗、护盾、命中 | 复用;核对固伤显式匹配、来源过滤和必要的效果分类透传 | +| `equipment/special-compiler.ts` | 已有封印概率转换为 sealResist 的范例 | 大衍复用同一规则尺度,不复制一套封印公式、不新增封印失败钩子 | +| `manuals/state.ts`、`shared/manuals/action.ts` | 学习、激活、修炼、解锁的纯规则已存在 | 保持流程;仅按收藏决策改上限或另立替换需求 | +| `items/definitions/manual-jades.ts` | 玉简 ID 自动派生 `jade.${manual.id}` | 新 ID 与旧资产切换绑定处理,不能删旧定义后才处理引用 | +| `rewards/wild-pack.ts` | 自动把所有玉简纳入 manualJades 组,权重读功法包 | 把供给名单/权重放到奖励配置,避免「新增内容即自动投放」 | +| `CombatV6ManualService.ts`、`characterLoadoutRepository.ts` | learned JSONB、槽位表、版本校验与事务 | 默认不改持久结构;保留角色锁、归属、revision、占用和提交后事件 | +| 功法页面与物品 preview | 效果偏六维,页面仍显示常见/珍稀 | 共用效果描述器,展示本层/下层六维与机制;去掉功法稀有度文案 | + +完整模块路径以 `src/shared/engine/combat-v6` 为基准;服务/仓储位于 `src/server/lib/services/combat-v6`、`src/server/lib/repositories`。 + +### 4.1 功法内容格式 + +建议数据包格式升为下一版,结构仅包含稳定身份、境界、意象描述、培养规则引用、单属性成长、单机制定义。机制用有限的 discriminated union 表达:伤害增幅、承伤减免、治疗增幅、护盾增幅、物理命中抵抗、封印抵抗、回合气血恢复、回合法力恢复。 + +各类型只开放需要的条件、类型范围、valueAt1/valueAt9。由编译器落实主目标/来源限制,避免策划漏填后把加成扩散到全场。阈值与效果文字由同一数据生成,意象描述单独保存。数据包不接受任意 JS、自由公式、嵌套条件树或 hook 脚本。 + +没有新增状态的机制不人为生成 StatusDef。若实现证明需要临时状态,才补投影状态表合并及 ID 冲突检查,不能预先搭建新框架。 + +静态内容包格式、战斗内容版本与角色 learned 状态 version 是不同概念,不能为了内容变更一并升级所有持久版本。 + +### 4.2 展示范围 + +- `ManualRoom.tsx`、`ManualRealmSlot.tsx`、`ManualJadePicker.tsx`:已学显示实际层数;修炼显示本层→下层的两部分变化;瓶颈明确仅解锁上限。 +- `presentation/basic.ts` 的 manualAdapter:继续「功法名 / 类型→传承境界→数量 / 所载功法效果 / 玉简描述」,不加重复功法名或第三层标题。 +- 未学习玉简显示一层效果和九层成长终点,不假装玩家已具有满层收益。已学上下文不得把玉简本身当成带层数的实例。 +- 共享描述器放在纯 shared 层,复用于页面、物品、公开构筑及查探。概率文案明确为加减百分点,UI 可采用规范允许的简洁符号表达,不能与相对比例混淆。 +- 不新增 rarity 徽章、品质筛选或难获取配色。不影响其他物品自己的品质体系。 +- 公开构筑只暴露激活功法和层数,不暴露收藏;保持现有布局、操作入口、响应式结构和 GameIcon 规范。 + +## 5. 分阶段实施与完成标准 + +阶段顺序为 A → B → C → D → E → F。B/C 可在纯领域数据夹具中先验证,E 的投放及旧资产切换在第 8 节决策落实前不执行。 + +### A. 冻结合同与引用清单 + +- 固化第 2、3 节内容;整理 24 本新 manualId/玉简 ID 清单。长春可评估保留原 ID,名称相近但境界不同的功法不能复用身份(例如原筑基厚土功与新炼气厚土诀)。 +- 盘点普通固伤、按目标血量比例效果、混合技能、主目标治疗/护盾、可解除封印的现行分类。 +- 梳理定义引用:已学记录、槽位、bag/storage、拍卖、邮件附件、宝库及其他奖励载体。只读盘点,不推断任何环境已为空。 +- 确定收藏、旧资产与首批投放决策;形成可审阅清单,再进入对应写入阶段。 + +完成标准:没有靠技能中文名、模糊「控制」或「固伤」描述才能判断的效果;新旧 ID 引用处理路线明确。 + +### B. 最小战斗能力与代表功法 + +- 在 shared 下为缺少的通用条件先写边界测试,再实现。 +- 用代表功法覆盖能力族:长春/清泉(恢复)、庚金(条件输出)、归元/太白(盾存在)、幻灵/千浪(命中)、大衍(封印尺度)、玄阴(状态+固伤)、玉丹/天罗(治疗与盾)、明王/浩然(自身状态)。 +- 验证普攻主目标、普通固伤与特殊扣血、器诀排除、派生伤害、保护、必中和恢复上限。 + +完成标准:不新增行动历史、专属计数器、core 功法 ID 分支;新增条件有独立确定性测试,现有相关 combat-v6 测试通过。 + +### C. 内容包、编译与角色投影 + +- 接入有限机制 schema、统一层数求值与描述器;移除 rarity。 +- 一次性录入 24 本,检查每境界六本、单属性单机制、范围/层数/引用有效。 +- 更新 schema、导出、纯领域测试;人物展示与真实入场使用同一投影。 +- 培养费用按末尾最新修订;保留玉简消耗、未激活功法可培养和战外切换规则;收藏变更只按已决策方案实施。 + +完成标准:所有功法 1~9 层值正确且单调,未激活不生效,永久六维不变;最高资源变化遵守 condition 规则;概率成长无截断台阶错误。 + +### D. 页面、玉简与公开展示 + +- 接入统一效果说明,去除功法 common/rare 所有展示分支。 +- 学习/升层/突破/切换的实际效果与描述一致,升级预览同时展示属性和机制差值。 +- 按现有物品预览规范检查背包、货架、聊天晒物等复用入口,不重构公共预览布局。 +- 覆盖本人、他人查探、未入宗个人面板与未开放境界位置。 + +完成标准:桌面与 360px 实际浏览器检查通过;无前端/服务端单测;旧 revision、资源不足和战斗占用时无多扣、多发或假成功。 + +### E. 产出配置与资产切换 + +- 供给名单与权重由奖励配置拥有。现有 fixed entries 已能显式配置玉简,可优先复用;不为本轮引入新掉落框架。 +- 删除玩法包中的 rarity 与供给耦合;manualJades 自动展开分支若因此无人使用才删除。更新 reward schema 与对应内容版本。 +- 既有玉简组 3% 总掉率是否维持,由投放清单记录。不要把 24 本全部等权自动投放视为已确认,也不在本轮默认新增地图或商店。 +- 按批准的新旧资产方案切换;必要的数据变更复用正式迁移/事务边界。不在计划阶段清空记录、不借机处理其他遗留系统。 +- 更新相关战斗内容版本。发布前安排旧活动战局正常排空或使用既有维护策略,保证恢复战局不混用两版功法;不清 Redis 战局逃避兼容。 +- 核对历史回放依赖归档内容还是重新投影,确保旧回放可读。迁移回滚要覆盖数据与内容版本,不能只回退代码。 + +完成标准:无未知功法/玉简引用、无错位槽位、无丢失进度/物品;产出与展示无 rarity 概念;未完成旧资产盘点与处置决策不得切换目标环境。 + +### F. 平衡与整体验收 + +- 在 shared 纯引擎测试中准备可重复样本,使用固定种子集合对随机命中/封印效果做比较;不创建独立 benchmark、seed、smoke 或故障注入脚本。 +- 比较三组:无功法、仅功法六维、完整功法。装备、灵兽、宗门、对手、初始资源与行动策略保持可比。 +- 覆盖炼气/筑基/金丹/元婴四阶段;高血物理、高血法术、残血魔道、固伤消耗、治疗护盾、抗控防守等组合。 +- 覆盖短战/长战、单体/群体/多段、带盾/无盾、带 DOT/无 DOT、封印/减益、人物+灵兽和队伍配合。 +- 记录有效伤害、实际治疗、有效吸盾、恢复法力、存活回合、控制命中及胜负分布。比例上界、无效触发和过量治疗不能冒充实际收益。 +- 初始目标是适配组合有感、整体贡献小于完整装备与正常出战灵兽;人物输出平均增幅约 8%~12% 作为观察目标,超过约 15% 的持续收益重点复核。机制瞬时约 20% 并不能豁免六维预算。不同定位不能直接用一个「总战力百分比」验收。 +- 检查低层恢复取整是否长期为零、防守/治疗组合是否形成拖延僵局、某一功法是否成为同位无条件最优。 + +完成标准:提交样本、命令与结果记录;未达目标先调整现有数值,不加复杂触发补丁。同步现行设计、产出说明与收尾清单,再标记完成。 + +## 6. 测试与检查清单 + +### 共享领域自动验证 + +- [ ] 数据包:24 本、四境界各六本、唯一 ID、单属性、单机制、无 rarity、无非法条件组合、引用完整。 +- [ ] 成长:全部 1~9 层;1/5/9 锚点、分数百分点精度、下一层差值;永久属性不变。 +- [ ] 血量/法力:阈值下方、恰好阈值、上方;伤势与满资源;低血多段跨阈值。 +- [ ] 护盾:不存在、零量、存在、最后一层打破、多层盾;归元与太白同场。 +- [ ] 状态:Dot/Debuff/Control 分离;不可驱散、休息、禁复活不冒充可解除封印。 +- [ ] 来源:人物普攻/宗门技能/器诀/灵兽/被动/派生/持续伤害;主目标与次目标。 +- [ ] 固伤:普通固伤得到预期加成,目标血量比例扣血与资源扣除不被放大;混合技能逐效果验证。 +- [ ] 恢复:回合末存活、倒地、禁疗、减疗、伤势、缺口、取整、战斗结束顺序;不触发主动治疗增幅。 +- [ ] 命中/封印:百分点加减、既有上下限、必中、免疫、其他来源同时存在;无额外随机调用。 +- [ ] 治疗/护盾:主目标、群体、过量、器诀排除、功法恢复排除。 +- [ ] 状态变更:学习/培养/切换/瓶颈/旧 revision/未激活/未开放境界;收藏规则按最终决策。 +- [ ] 恢复与重放:相同种子、输入及战局快照得到一致结果,保存恢复不丢条件事实。 +- [ ] 产出:只含显式投放项,权重独立于功法强度,未知/重复奖励拒绝。 + +### 本地流程验收 + +按 [测试规范](testing.md) 复用本地测试账号,完成真实学习、升层、瓶颈、切换、刷新、查探与一场战斗。仅在 local 环境准备测试资源。核对当前资源不因面板变化免费补满,失败请求不扣资源,浏览器多页使用旧版本提交时正确刷新。 + +不在 `src/react-app`、`src/server` 新增 test/spec;不 mock 数据库、API、Redis、SMTP 或其他第三方服务。 + +建议实施检查命令: + +```bash +bun run test src/shared/engine/combat-v6/manuals src/shared/manuals +bun run test src/shared/engine/combat-v6/core src/shared/engine/combat-v6/projection src/shared/engine/combat-v6/rules-daoyou +bun run lint +bun run test +bun run build +git diff --check +``` + +先执行与当前改动对应的聚焦测试;本轮涉及通用条件和投影,最终需要全量 shared 回归。build 已包含 client/server 类型检查。schema 更新使用项目已有 Zod toJSONSchema 方式,不手写与源结构不一致的 schema。 + +## 7. 文档与交付物 + +- 本文:内容、边界、阶段、验收与待决策项。 +- 实施后更新 `combat-v6-manual-system-design.md`:正式替代原 16 本属性版,不删除历史验收事实。 +- 实施后更新 `combat-v6-polish-and-closure-plan.md` 中功法供给/平衡条目,链接实际验收记录。 +- 如果收藏规则、产出或资产方案变更,先更新本文相关决定,避免代码与计划各自解释。 +- 每阶段报告修改文件、执行命令、结果及未验项;没有本地真实流程证据不能标记完整交付。 + +## 8. 已落实的切换决定 + +用户明确要求删除旧定义、删除旧数据、不要兼容代码,并按推荐开始实施。 + +- 每境界收藏上限六本,同位只激活一本。 +- 旧 16 本定义被新 24 本替换;不做 ID 映射、退款或旧状态适配。 +- 迁移 `0049_manual_first_release.sql` 清空旧槽位、learned、库存玉简、拍卖玉简及邮件/兑换码玉简附件。保留状态 revision 并递增,防止旧请求重放;邮件其他附件不受影响。 +- 初次产出沿用现有野外渠道与 3% 组概率,24 本各权重 100,配置由奖励包显式维护。 +- 仅本地数据库已执行迁移。部署其他环境应停止旧版本写入、排空活动战局、执行迁移后切换版本;迁移不可仅靠回退代码还原。 +- 冻结历史回放保留,不是可学习、领取或交易的功法数据;无需旧功法运行时兼容。 + +## 9. 实施验证(2026-09-16) + +- 运行配置为 manual pack v3,野外 contentRevision/poolVersion 同步递增。没有改动原始 Excel。 +- 本地迁移后、发放新验收玉简前:槽位 0;两个状态 learned 均为空;库存、拍卖、邮件、兑换码中的旧玉简引用均为 0。 +- 已执行聚焦功法/奖励/公开构筑测试、全量 shared 测试、lint、TypeScript 及客户端/服务端构建。全量结果:168 个文件通过,1390 项通过;仅原有炼体标签快照失败(src/shared/lib/bodyCultivation/pack.test.ts:20),该目录本次无修改。聚焦机制测试 20 项通过。lint、build、tsc 与 git diff --check 均通过。 +- 本地已有账号“本地道友1”真实页面验收:新玉简学习、首本自动激活、后续收藏不覆盖、六本上限隐藏学习入口、免费切换、刷新保留、长春一升二/二升三、三层玉简解锁、感悟不足禁用修炼。当前气血未因属性增长补满。 +- 学习详情与升级确认同时展示六维、当前/下一层/圆满机制。首屏保持简洁,完整机制放在参悟与物品预览层。 +- 九层成长、六层瓶颈、条件边界、来源/主目标过滤、比例固伤排除、状态分类、回合恢复和封印小数精度由纯共享测试覆盖。 +- 未完成真实四槽跨宗门对战强度矩阵、线上产出周期测算、断线恢复和多页并发验收;这些不标记为通过。尚不能宣称功法相对装备/灵兽的长期平衡已验证。 + +### 浏览器与清理补充 + +桌面和 360px 页面及机制详情已检查,浏览器 error 日志为空。六本收藏、切换和升级结果持久化正常;本次未使用的验收玉简已清理,真实学习培养进度保留。未调整准备性境界、六维或修炼资源。 + +### 基础属性调优 + +根据用户关于基础属性感受不明显的反馈,将炼气/筑基每层提高至 +2,金丹/元婴每层提高至 +4;四槽满层由合计 54 点提高至 108 点。机制数值与培养成本不变,现有层数直接按新配置投影,无需重置进度或执行清库迁移。 + +本次数值调整验证:功法/投影/培养操作/公开构筑/奖励聚焦测试 11 个文件、87 项通过;lint 与 diff 检查通过。仅修改成长配置和对应预期,未重新执行全量测试或浏览器流程。 + +### 入门属性基数 + +根据后续反馈,采用一层初始值+后续每层成长:`valueAt1 + (level - 1) × valuePerLevel`。一层直接获得 12/18/24/36 点,不额外加一次成长;九层分别 28/34/56/68 点。数据包结构升为 v3,valueAt1 必填,不加旧格式兼容。投影、槽位展示、玉简与参悟预览复用同一计算函数;层数、成本及机制不变,不执行清库迁移。此前数值调优记录保留为历史。 + +### 升级消耗修订 + +按用户确认的档位,仅调整后续升级费用。L 为升级前层数(1~8),一层学习和瓶颈解锁仍不扣修为、感悟。 + +| 归属境界 | 单次修为 | 单次感悟 | 九层累计修为 | 九层累计感悟 | +| --- | ---: | ---: | ---: | ---: | +| 炼气 | 40 × L | 2 × (L + 1) | 1,440 | 88 | +| 筑基 | 200 × L | 3 × (L + 1) | 7,200 | 132 | +| 金丹 | 1,000 × L | 4 × (L + 1) | 36,000 | 176 | +| 元婴 | 4,000 × L | 5 × (L + 1) | 144,000 | 220 | + +不调整玉简供给、基础属性、机制、层数和感悟上限。现有进度保留,不追溯补扣费用,不执行数据迁移。页面升级预览和服务端扣费均通过 previewManualAction 读取同一配置。 + +本次费用修订验证:`bun run test` 聚焦功法、培养操作、投影、公开构筑及奖励目录,11 个文件、92 项通过;覆盖四境界总预算、八升九精确余额、资源不足拒绝及高境界补修低境界的费用。`bun run lint`、`bun run build`、`git diff --check` 通过。本次未重复全量测试或浏览器流程。 + +### 玉简消耗修订 + +学习消耗 1 本,三层瓶颈消耗 2 本,六层瓶颈消耗 3 本,单本功法圆满共需 6 本同名玉简。共享数量规则统一驱动预览校验、选择器、确认文案和服务端事务扣减;数量不足拒绝突破,数量恰好耗尽删除库存行,有剩余则保留并递增版本。已有解锁不追补,修为及感悟档位不变。 + +本次玉简修订验证:功法与培养操作聚焦测试 4 个文件、48 项通过;lint、前后端 build、git diff --check 通过。本地道友1真实页面验收三层仅1本时不可选、持有5本突破扣2本剩3本、六层突破扣3本归零并解锁九层;数据库核对该玉简库存行已删除。五次真实升级共扣600修为和40感悟,未增加修为;准备性感悟已恢复原值2,保留实际培养进度,无剩余验收玉简,浏览器 error 日志为空。本轮未重复全量测试。 diff --git a/docs/combat-v6-manual-system-design.md b/docs/combat-v6-manual-system-design.md index 7c5d5f93..5b3db8e5 100644 --- a/docs/combat-v6-manual-system-design.md +++ b/docs/combat-v6-manual-system-design.md @@ -3,17 +3,19 @@ > 2026-09-10:经用户确认,替代 Phase 5A/9F 的本篇、真解和覆盖散功方案。无旧数据迁移或兼容。 > 上位设计:[改造路线图](./combat-v6-mhxy-redesign-roadmap.md)。 +> 2026-09-16:首版 24 本功法已实现。每本六维+单条件机制同步成长,取消稀有度;每位最多收藏六本。具体数值与验证记录见[首版功法改造计划](combat-v6-manual-first-release-plan.md)。 + ## 玩法规则 - 炼气、筑基、金丹、元婴各开放一位,总共四位;更高境界不增加位置。 - 功法有固定境界归属,只能在对应位置激活。效果类型不按境界硬限制,由内容设计体现方向差异。 -- 每个位置最多学习三种功法,每本独立保存层数和已解锁上限,同时只激活一本。暂不支持遗忘或替换;学满后不能继续学习新的功法,同名玉简突破不占额外名额。 +- 每个位置最多学习六种功法,每本独立保存层数和已解锁上限,同时只激活一本。暂不支持遗忘或替换;学满后不能继续学习新的功法,同名玉简突破不占额外名额。 - 学习消耗一本同名玉简,从第一层开始。空位自动激活;已有激活功法时,新学功法保存在收藏中。 - 免费切换,无冷却,不丢失培养进度。沿用现有战斗/结算占用保护,战斗结束后才能操作,下一场生效。 -- 默认九层,三层、六层消耗一本同名玉简突破瓶颈,只解锁后续层数,不直接升级。三本玉简加八次修炼完成一本功法。 +- 默认九层,三层、六层分别消耗两本、三本同名玉简突破瓶颈,只解锁后续层数,不直接升级。六本玉简加八次修炼完成一本功法。 - 升层消耗当前可用修为与道心感悟;修为继续用于境界提升,不能透支或导致境界倒退。功法满层不是角色突破条件。 - 未达到功法对应境界时,可持有和交易玉简,不能学习或培养。 -- 未激活功法允许培养。第一版只有六维固定属性加成,稀有功法可同时提供两项效果,两项从一层同时生效。 +- 未激活功法允许培养。每本提供一项六维属性与一个机制,从一层同时生效,九层内均逐层增强。 ## 策划数据包 @@ -29,15 +31,14 @@ | costsByRealm | 各功法归属境界的逐层成本数组;第一项为一升二,必须覆盖所有后续层 | | id | 稳定功法 ID,同时派生玉简和被动 ID | | realm | 功法位归属 | -| rarity | common/rare,显示用,不强制效果数量 | -| effects | 一至两项不重复的六维属性;valuePerLevel 为每层固定加成 | -| dropWeight | 现有野外玉简组内相对权重,与稀有度独立配置 | +| effects | 一项六维属性;valueAt1 为一层初始加成,valuePerLevel 为后续每升一层的增量 | +| mechanism | 机制类型、单条件、适用伤害种类(如需要)、valueAt1 / valueAt9;九层线性插值 | -当前十六本:每境界三本常见单属性、一本珍稀双属性。炼气/筑基每项每层 +1,金丹/元婴每项每层 +2。四本珍稀全部九层提供六维合计 108 点,是初始调参基线,不代表长期平衡结论。 +当前 24 本,每境界六本。炼气/筑基/金丹/元婴一层分别 +12/18/24/36,后续每层分别 +2/2/4/4,四本激活功法全部九层提供六维合计 186 点。机制包括条件增伤、减伤、治疗/护盾增强、闪避、封印抵抗和回合末恢复,详见首版计划。数值是首轮调参基线,尚非长期平衡结论。 -默认修为逐层费用:炼气 20、筑基 100、金丹 500、元婴 2000,分别乘以当前层数;道心感悟二至九层分别消耗 2 至 9。全本共消耗基础修为费用的 36 倍与 44 感悟。成本按功法归属境界计算,不因角色升境变化。 +修为逐层费用:炼气 40、筑基 200、金丹 1000、元婴 4000,分别乘以当前层数 L(1~8);感悟费用分别为 2/3/4/5 × (L + 1)。单本九层累计修为分别 1,440/7,200/36,000/144,000,累计感悟分别 88/132/176/220。最高单次感悟消耗为 45,不改变 100 点储存上限。成本按功法归属境界计算,不因角色升境变化;旧已完成升级不追溯补扣。 -野外既有玉简组总掉落率保持 3%,常见权重 100、珍稀权重 5;现有池暂覆盖全部十六本,每本珍稀的相对掉率为常见的 1/20。玉简沿用统一库存、堆叠和拍卖交易。暂不新增获取渠道。 +野外既有玉简组总掉落率保持 3%,24 本玉简以 fixed 条目显式配置,初始各权重 100。功法包不再存稀有度或产出权重,产出包独立决定供应。玉简沿用统一库存、堆叠和拍卖交易;暂不新增渠道。 修改内容无需修改 TypeScript;新增效果机制才扩展编译器。Schema 结构源为 ManualPackShape,结构变更后通过 Zod toJSONSchema 更新同目录 schema;重复 ID、重复属性、缺失引用和不完整成本表由运行时细化校验拒绝。 @@ -45,26 +46,26 @@ - 已学进度保存在功法状态的 learned JSONB;激活位置沿用独立槽位表,不创建多套构筑。 - 纯 changeManual 与 previewManualAction 产生确定性状态和费用计划。服务端负责身份、玉简归属、版本、战斗占用及资源事实。 -- 每境界三种上限由共享状态校验与学习规则同时检查,服务端在扣减玉简前执行,不只依赖前端隐藏学习入口。 +- 每境界六种上限由共享状态校验与学习规则同时检查,服务端在扣减玉简前执行,不只依赖前端隐藏学习入口。 - 角色锁、数据库事务与版本比较覆盖扣费、玉简消费、功法进度、激活位置和资源事件;任一步失败整体回滚。 -- 每本激活功法编译一个被动标识和一至两项六维贡献。贡献在装备/宗门/炼体面板计算前加入临时属性,派生值遵循现有 v6 公式,不写入永久六维。 -- 被动身份用于记录来源与层数;属性修正由人物编译器应用一次,不再在战斗开始时重复增加。 +- 每本激活功法编译一个随层数编译的机制被动和一项六维贡献。贡献在装备/宗门/炼体面板计算前加入临时属性,派生值遵循现有 v6 公式,不写入永久六维。 +- 被动负责单条件机制结算并记录来源与层数;属性修正由人物编译器应用一次,不再在战斗开始时重复增加。 - 公开构筑只显示激活功法与层数,不暴露未激活收藏或培养记录。 -- 需要应用迁移 `0043_manual-level-progression.sql`;没有旧功法数据兼容。历史 5A/9F 文档保留为阶段记录,不再作为当前规则。 +- 需要应用迁移 `0049_manual_first_release.sql`:清空旧已学进度、槽位、库存玉简、拍卖与邮件/兑换码中的玉简附件;不映射、不退款、不留旧定义兼容。冻结历史战斗归档保留,自包含快照不作为可用功法资产。切换环境应先停止旧版本写入并排空活动战局,再执行迁移与部署。历史 5A/9F 文档保留为阶段记录,不再作为当前规则。 ## 功法页面(2026-09-11) -- 左侧按角色性别展示透明底盘坐墨像,右侧自上而下固定排列炼气、筑基、金丹、元婴四位。桌面与窄屏均保持左右关系,墨像完整显示,无混合模式或整体透明度修补。 +- 左侧按角色性别展示透明底盘坐墨像,右侧自上而下固定排列炼气、筑基、金丹、元婴四位。桌面保持左右关系,窄屏纵向排列,墨像完整显示,无混合模式或整体透明度修补。 - 未学习时显示空槽,未开放时显示对应境界提示,不铺开全部功法配置。 - 每位仅显示当前激活功法、层数、固定属性贡献和参悟入口。两种以上已学功法显示箭头与圆点,左右触摸滑动或箭头点击直接免费切换;提交期间禁用重复操作,服务端成功后更新显示,失败保留或重新读取权威状态。 -- 未满三种时显示学习入口;学满后隐藏入口,仍可培养或切换。学习确认层明确告知三种上限、暂不支持遗忘或替换。 +- 未满六种时显示学习入口;学满后隐藏入口,仍可培养或切换。学习确认层明确告知六种上限、暂不支持遗忘或替换。 - 学习及瓶颈突破复用公共 InventoryItems/ItemSlot 与 InkDetailDrawer;只提供对应境界可学习玉简或同名突破玉简。玩家明确选中具体玉简后再确认消费,不自动挑选库存。 -- 学习第一本自动激活,第二、第三本保留原激活项。培养成本、层数与属性配置保持现有规则。 +- 学习第一本自动激活,后续各本保留原激活项。培养成本、层数与属性配置保持现有规则。 - 美术生成提示词、透明处理和生产文件见 [功法墨像素材记录](./combat-v6-manual-portrait-assets.md)。 ## 验证 -共享规则测试覆盖境界位、三本玉简九层成长、资源不足、版本冲突、收藏独立进度、免费切换、非法状态、双属性投影、数据包校验与现有掉落权重。服务端和页面采用静态检查及真实本地流程验证,不添加数据库或 API 单元测试。 +共享规则测试覆盖境界位、六本玉简九层成长、资源不足、版本冲突、收藏独立进度、免费切换、非法状态、双属性投影、数据包校验与现有掉落权重。服务端和页面采用静态检查及真实本地流程验证,不添加数据库或 API 单元测试。 ### 本地验收(2026-09-10) diff --git a/docs/combat-v6-polish-and-closure-plan.md b/docs/combat-v6-polish-and-closure-plan.md index 69ca78ce..b342643c 100644 --- a/docs/combat-v6-polish-and-closure-plan.md +++ b/docs/combat-v6-polish-and-closure-plan.md @@ -59,7 +59,7 @@ R08依赖R01~R07及首发范围内的核心场景通过;不能仅靠健康 | G05 P1 待验/待调优 | 六种突破、金丹以上幻境、全境界排名主要有链路证据,强化测试属性不能证明正常难度 | 先确定正式可得装备/功法/修炼/心法样本,再制定目标回合、失败率、恢复消耗与收益范围;比较清心前后心魔、同级和跨档挑战,记录实际结果再调系数 | [10F](combat-v6-phase-10f-breakthrough-plan.md)、[10B](combat-v6-phase-10b-tower-plan.md)、[10C](combat-v6-phase-10c-ranking-plan.md) | | G06 P1 待验/待调优 | 五宗门双流派已实现,器蕴/器诀逐项实战、同队手动AUTO、特殊状态组合未全验 | 覆盖五宗十流派、关键经脉、复活/伤势/护盾/五行/形态和器诀,优先容易叠加或重复计效的组合;纯确定性边界使用shared测试,真实页面验证可选、可用及说明一致 | [宗门设计](combat-v6-sect-skill-meridian-system-design.md)、[9D](combat-v6-phase-9d-forging-plan.md)、[10E](combat-v6-phase-10e-auto-sect-tasks-plan.md) | | G07 P1 待调优 | 炼体0~60级节奏、丹毒、配方/灵果供给没有整体经济验证;心法经脉成本已配置但未整体平衡 | 对照正式来源核算升级所需时间、药力、灵石、修为和感悟;发现不可达或明显失衡再改配置。保留消耗品提升五轨、确定性位阶晋升,不新增直接点修 | [修炼设计§13](combat-v6-training-system-design.md#13-后续平衡项)、[宗门设计§13](combat-v6-sect-skill-meridian-system-design.md#13-后续专项平衡项) | -| G08 P1 待调优 | 新功法四境界位、16本九层已完成;全部玉简暂共用青溪3%组,珍稀权重为常见1/20,没有新渠道 | 评估三本同名玉简与八次修炼的实际养成时间、境界供给和交易价格;先决定分层掉落或兑换是否必要,再扩渠道。只调新版层数和成本,不回到本篇/真解 | [功法设计](combat-v6-manual-system-design.md)、[wild.ts](../src/shared/rewards/wild.ts) | +| G08 P1 待调优 | 新功法四境界位、24本九层、每位收藏六本已实现;六维与单条件机制同步成长;全部玉简暂共用青溪3%组、各权重100,没有稀有度或新渠道 | 评估三本同名玉简与八次修炼的实际养成时间、境界供给和交易价格;先决定分层掉落或兑换是否必要,再扩渠道。按[首版验证记录](combat-v6-manual-first-release-plan.md)继续验证机制收益及其低于装备/灵兽的整体预算 | [功法设计](combat-v6-manual-system-design.md)、[wild.ts](../src/shared/rewards/wild.ts) | | G09 P1 待调优 | 蜃楼物资仍复用凡品固定材料,高境界收益未调;掉落长期经济样本不足 | 联合G02/G05建立野外、秘境、蜃楼、宗门、炼器与NPC回收的产销对照,关注资源通胀与无用途产物;每个开放奖励有实际用途,记录版本与既有快照边界 | [10B](combat-v6-phase-10b-tower-plan.md)、[tower奖励](../src/shared/rewards/tower.ts)、[9E](combat-v6-phase-9e-drops-plan.md) | | G10 P1 待验 | 多人/展示:旧8A曾有真实4v4,后续带宠、观众、慢连接与新协议未完成全矩阵 | 8个独立角色每人1宠、另设观众;混合手动/AUTO、倒地、复活、多备用宠换宠、逃跑、重连与终局;确认公开视图不泄露精确属性,按首发容量测请求与播放积压 | [9A](combat-v6-phase-9a-summoned-beast-plan.md)、[8C](combat-v6-phase-8c-spectator-design.md)、[战斗UI](combat-v6-battle-ui.md) | | G11 P1 部分已验 | 10Z已验证恢复丹具体堆叠消耗、人物MP跨场继承、换宠后两只灵兽实际经验到账、真实战败刷新结算;修正休整旧库存来源和扣药占用冲突 | 继续补灵兽MP实际消耗与死亡本轮回归、全地图高等级、任务消费者并发去重及完整状态/构筑入口互斥矩阵;不把部分入口通过外推全部 | [10Z验收](combat-v6-phase-10z-dungeon-continuity.md)、[10A](combat-v6-phase-10a-dungeon-plan.md#待专项验收) | diff --git a/drizzle/0049_manual_first_release.sql b/drizzle/0049_manual_first_release.sql new file mode 100644 index 00000000..30ad3666 --- /dev/null +++ b/drizzle/0049_manual_first_release.sql @@ -0,0 +1,39 @@ +-- Unreleased manual prototype: discard learned progress and all old jade assets. +-- Run during maintenance before the 24-manual content is made available. +DELETE FROM "wanjiedaoyou_cultivator_manual_slots"; +--> statement-breakpoint +-- Keep the concurrency revision monotonic so a pre-reset request cannot replay. +UPDATE "wanjiedaoyou_cultivator_manual_states" +SET "learned" = '[]'::jsonb, "revision" = "revision" + 1, "updated_at" = now(); +--> statement-breakpoint +DELETE FROM "wanjiedaoyou_inventory_items" +WHERE starts_with("definition_id", 'jade.character_manual.'); +--> statement-breakpoint +DELETE FROM "wanjiedaoyou_auction_listings" +WHERE "item_type" = 'manual_jade' + OR starts_with("item_snapshot" #>> '{item,definitionId}', 'jade.character_manual.'); +--> statement-breakpoint +-- Remove only jade attachments; retain the mail and any unrelated rewards. +UPDATE "wanjiedaoyou_mails" AS m +SET "attachments" = ( + SELECT coalesce(jsonb_agg(a.value ORDER BY a.ordinality), '[]'::jsonb) + FROM jsonb_array_elements(m."attachments") WITH ORDINALITY AS a(value, ordinality) + WHERE NOT coalesce(starts_with(a.value #>> '{inventory,definitionId}', 'jade.character_manual.'), false) +) +WHERE jsonb_typeof(m."attachments") = 'array' + AND EXISTS ( + SELECT 1 FROM jsonb_array_elements(m."attachments") AS a(value) + WHERE starts_with(a.value #>> '{inventory,definitionId}', 'jade.character_manual.') + ); +--> statement-breakpoint +UPDATE "wanjiedaoyou_redeem_codes" AS r +SET "reward_attachments" = ( + SELECT coalesce(jsonb_agg(a.value ORDER BY a.ordinality), '[]'::jsonb) + FROM jsonb_array_elements(r."reward_attachments") WITH ORDINALITY AS a(value, ordinality) + WHERE NOT coalesce(starts_with(a.value #>> '{inventory,definitionId}', 'jade.character_manual.'), false) +) +WHERE jsonb_typeof(r."reward_attachments") = 'array' + AND EXISTS ( + SELECT 1 FROM jsonb_array_elements(r."reward_attachments") AS a(value) + WHERE starts_with(a.value #>> '{inventory,definitionId}', 'jade.character_manual.') + ); diff --git a/drizzle/meta/0049_snapshot.json b/drizzle/meta/0049_snapshot.json new file mode 100644 index 00000000..6abb68b0 --- /dev/null +++ b/drizzle/meta/0049_snapshot.json @@ -0,0 +1,8420 @@ +{ + "id": "3152364d-bd3b-413e-ac05-a52649c16947", + "prevId": "e6bcb39c-d795-43d8-93fd-959dc92996d3", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.wanjiedaoyou_account_deletion_records": { + "name": "wanjiedaoyou_account_deletion_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cultivator_ids": { + "name": "cultivator_ids", + "type": "uuid[]", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "requested_at": { + "name": "requested_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "account_deletion_records_user_uidx": { + "name": "account_deletion_records_user_uidx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "account_deletion_records_status_requested_idx": { + "name": "account_deletion_records_status_requested_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "requested_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_admin_message_templates": { + "name": "wanjiedaoyou_admin_message_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "channel": { + "name": "channel", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "subject_template": { + "name": "subject_template", + "type": "varchar(300)", + "primaryKey": false, + "notNull": false + }, + "content_template": { + "name": "content_template", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "default_payload": { + "name": "default_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "admin_templates_channel_status_created_idx": { + "name": "admin_templates_channel_status_created_idx", + "columns": [ + { + "expression": "channel", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_alchemy_formulas": { + "name": "wanjiedaoyou_alchemy_formulas", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "family": { + "name": "family", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "pattern": { + "name": "pattern", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "blueprint": { + "name": "blueprint", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "mastery": { + "name": "mastery", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"level\":0,\"exp\":0}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "alchemy_formulas_cultivator_updated_idx": { + "name": "alchemy_formulas_cultivator_updated_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "alchemy_formulas_cultivator_family_idx": { + "name": "alchemy_formulas_cultivator_family_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "family", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_alchemy_formulas_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_alchemy_formulas_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_alchemy_formulas", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_app_settings": { + "name": "wanjiedaoyou_app_settings", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "varchar(128)", + "primaryKey": true, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_auction_listings": { + "name": "wanjiedaoyou_auction_listings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "seller_id": { + "name": "seller_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "seller_name": { + "name": "seller_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "item_type": { + "name": "item_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "item_id": { + "name": "item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "item_name": { + "name": "item_name", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "item_quality": { + "name": "item_quality", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "item_category": { + "name": "item_category", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "item_snapshot": { + "name": "item_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "initial_quantity": { + "name": "initial_quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "remaining_quantity": { + "name": "remaining_quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "visibility": { + "name": "visibility", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "target_cultivator_id": { + "name": "target_cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "target_cultivator_name": { + "name": "target_cultivator_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "sold_at": { + "name": "sold_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "auction_status_expires_created_idx": { + "name": "auction_status_expires_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_seller_status_idx": { + "name": "auction_seller_status_idx", + "columns": [ + { + "expression": "seller_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_status_expires_price_idx": { + "name": "auction_status_expires_price_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "price", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_status_type_expires_created_idx": { + "name": "auction_status_type_expires_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_status_expires_item_type_idx": { + "name": "auction_status_expires_item_type_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_status_type_category_expires_created_idx": { + "name": "auction_status_type_category_expires_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_status_type_quality_expires_created_idx": { + "name": "auction_status_type_quality_expires_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_quality", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_status_item_name_expires_created_idx": { + "name": "auction_status_item_name_expires_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "item_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_status_seller_name_expires_created_idx": { + "name": "auction_status_seller_name_expires_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "seller_name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "auction_visibility_target_status_idx": { + "name": "auction_visibility_target_status_idx", + "columns": [ + { + "expression": "visibility", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_auction_listings_seller_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_auction_listings_seller_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_auction_listings", + "columnsFrom": [ + "seller_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_auction_listings_target_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_auction_listings_target_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_auction_listings", + "columnsFrom": [ + "target_cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_battle_records_v3": { + "name": "wanjiedaoyou_battle_records_v3", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "opponent_cultivator_id": { + "name": "opponent_cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "battle_type": { + "name": "battle_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'normal'" + }, + "battle_result": { + "name": "battle_result", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "share_code": { + "name": "share_code", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "shared_at": { + "name": "shared_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "battle_records_v3_cultivator_created_idx": { + "name": "battle_records_v3_cultivator_created_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "battle_records_v3_opponent_created_idx": { + "name": "battle_records_v3_opponent_created_idx", + "columns": [ + { + "expression": "opponent_cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "battle_records_v3_user_created_idx": { + "name": "battle_records_v3_user_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "battle_records_v3_share_code_uidx": { + "name": "battle_records_v3_share_code_uidx", + "columns": [ + { + "expression": "share_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_battle_records_v3_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_battle_records_v3_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_battle_records_v3", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_battle_records_v3_opponent_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_battle_records_v3_opponent_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_battle_records_v3", + "columnsFrom": [ + "opponent_cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_battle_replay_archives": { + "name": "wanjiedaoyou_battle_replay_archives", + "schema": "", + "columns": { + "match_id": { + "name": "match_id", + "type": "varchar(120)", + "primaryKey": true, + "notNull": true + }, + "replay_version": { + "name": "replay_version", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "engine_version": { + "name": "engine_version", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "ruleset_version": { + "name": "ruleset_version", + "type": "varchar(60)", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "participants": { + "name": "participants", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "replay": { + "name": "replay", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "battle_replay_archives_finished_idx": { + "name": "battle_replay_archives_finished_idx", + "columns": [ + { + "expression": "finished_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "battle_replay_archives_participants_gin_idx": { + "name": "battle_replay_archives_participants_gin_idx", + "columns": [ + { + "expression": "participants", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "gin", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_bet_battles": { + "name": "wanjiedaoyou_bet_battles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "creator_id": { + "name": "creator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "creator_name": { + "name": "creator_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "min_realm": { + "name": "min_realm", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "max_realm": { + "name": "max_realm", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "taunt": { + "name": "taunt", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "creator_stake_snapshot": { + "name": "creator_stake_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "challenger_stake_snapshot": { + "name": "challenger_stake_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "challenger_id": { + "name": "challenger_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "challenger_name": { + "name": "challenger_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "winner_cultivator_id": { + "name": "winner_cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "battle_record_v3_id": { + "name": "battle_record_v3_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "matched_at": { + "name": "matched_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "settled_at": { + "name": "settled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "bet_battles_status_expires_idx": { + "name": "bet_battles_status_expires_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "bet_battles_creator_status_idx": { + "name": "bet_battles_creator_status_idx", + "columns": [ + { + "expression": "creator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "bet_battles_status_created_idx": { + "name": "bet_battles_status_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_bet_battles_creator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_bet_battles_creator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_bet_battles", + "columnsFrom": [ + "creator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_bet_battles_challenger_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_bet_battles_challenger_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_bet_battles", + "columnsFrom": [ + "challenger_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "wanjiedaoyou_bet_battles_winner_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_bet_battles_winner_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_bet_battles", + "columnsFrom": [ + "winner_cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "wanjiedaoyou_bet_battles_battle_record_v3_id_wanjiedaoyou_battle_records_v3_id_fk": { + "name": "wanjiedaoyou_bet_battles_battle_record_v3_id_wanjiedaoyou_battle_records_v3_id_fk", + "tableFrom": "wanjiedaoyou_bet_battles", + "columnsFrom": [ + "battle_record_v3_id" + ], + "tableTo": "wanjiedaoyou_battle_records_v3", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_breakthrough_history": { + "name": "wanjiedaoyou_breakthrough_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "from_realm": { + "name": "from_realm", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "from_stage": { + "name": "from_stage", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "to_realm": { + "name": "to_realm", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "to_stage": { + "name": "to_stage", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "age": { + "name": "age", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "years_spent": { + "name": "years_spent", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "story": { + "name": "story", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "breakthrough_history_cultivator_created_idx": { + "name": "breakthrough_history_cultivator_created_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_breakthrough_history_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_breakthrough_history_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_breakthrough_history", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_combat_replay_archives": { + "name": "wanjiedaoyou_combat_replay_archives", + "schema": "", + "columns": { + "battle_id": { + "name": "battle_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "metadata_version": { + "name": "metadata_version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source_type": { + "name": "source_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "battle_type": { + "name": "battle_type", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "engine_version": { + "name": "engine_version", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "ruleset_version": { + "name": "ruleset_version", + "type": "varchar(60)", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "finished_at": { + "name": "finished_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "outcome": { + "name": "outcome", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true + }, + "replay": { + "name": "replay", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "round_count": { + "name": "round_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sides": { + "name": "sides", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[[],[]]'::jsonb" + }, + "archived_at": { + "name": "archived_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "combat_replay_source_idempotency_uidx": { + "name": "combat_replay_source_idempotency_uidx", + "columns": [ + { + "expression": "source_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "idempotency_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "combat_replay_finished_idx": { + "name": "combat_replay_finished_idx", + "columns": [ + { + "expression": "finished_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_combat_replay_participants": { + "name": "wanjiedaoyou_combat_replay_participants", + "schema": "", + "columns": { + "battle_id": { + "name": "battle_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "side": { + "name": "side", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "combat_replay_participant_cultivator_idx": { + "name": "combat_replay_participant_cultivator_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "battle_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_combat_replay_participants_battle_id_wanjiedaoyou_combat_replay_archives_battle_id_fk": { + "name": "wanjiedaoyou_combat_replay_participants_battle_id_wanjiedaoyou_combat_replay_archives_battle_id_fk", + "tableFrom": "wanjiedaoyou_combat_replay_participants", + "columnsFrom": [ + "battle_id" + ], + "tableTo": "wanjiedaoyou_combat_replay_archives", + "columnsTo": [ + "battle_id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "wanjiedaoyou_combat_replay_participants_battle_id_cultivator_id_pk": { + "name": "wanjiedaoyou_combat_replay_participants_battle_id_cultivator_id_pk", + "columns": [ + "battle_id", + "cultivator_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_consumables": { + "name": "wanjiedaoyou_consumables", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "quality": { + "name": "quality", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'凡品'" + }, + "spec": { + "name": "spec", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "stack_key": { + "name": "stack_key", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "score": { + "name": "score", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "consumables_cultivator_idx": { + "name": "consumables_cultivator_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "consumables_cultivator_name_quality_idx": { + "name": "consumables_cultivator_name_quality_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "quality", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "consumables_cultivator_stack_key_idx": { + "name": "consumables_cultivator_stack_key_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stack_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "consumables_cultivator_stack_unique": { + "name": "consumables_cultivator_stack_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "quality", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stack_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"wanjiedaoyou_consumables\".\"stack_key\" is not null", + "concurrently": false + }, + "consumables_score_idx": { + "name": "consumables_score_idx", + "columns": [ + { + "expression": "score", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_consumables_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_consumables_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_consumables", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_creation_products": { + "name": "wanjiedaoyou_creation_products", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "product_type": { + "name": "product_type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "element": { + "name": "element", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "quality": { + "name": "quality", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "slot": { + "name": "slot", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "score": { + "name": "score", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "is_equipped": { + "name": "is_equipped", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "product_model": { + "name": "product_model", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "creation_products_cultivator_type_idx": { + "name": "creation_products_cultivator_type_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "product_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "creation_products_type_score_idx": { + "name": "creation_products_type_score_idx", + "columns": [ + { + "expression": "product_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "score", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "creation_products_equipped_idx": { + "name": "creation_products_equipped_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_equipped", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_creation_products_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_creation_products_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_creation_products", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivator_beast_lineups": { + "name": "wanjiedaoyou_cultivator_beast_lineups", + "schema": "", + "columns": { + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "lineup": { + "name": "lineup", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "starter_claimed_at": { + "name": "starter_claimed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "wanjiedaoyou_cultivator_beast_lineups_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_beast_lineups_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_beast_lineups", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivator_beasts": { + "name": "wanjiedaoyou_cultivator_beasts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "individual": { + "name": "individual", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cultivator_beasts_owner_idx": { + "name": "cultivator_beasts_owner_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_cultivator_beasts_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_beasts_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_beasts", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivator_equipment_slots": { + "name": "wanjiedaoyou_cultivator_equipment_slots", + "schema": "", + "columns": { + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slot": { + "name": "slot", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "equipment_instance_id": { + "name": "equipment_instance_id", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "cultivator_equipment_instance_unique": { + "name": "cultivator_equipment_instance_unique", + "columns": [ + { + "expression": "equipment_instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_cultivator_equipment_slots_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_equipment_slots_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_equipment_slots", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_cultivator_equipment_slots_cultivator_id_equipment_instance_id_wanjiedaoyou_inventory_items_cultivator_id_id_fk": { + "name": "wanjiedaoyou_cultivator_equipment_slots_cultivator_id_equipment_instance_id_wanjiedaoyou_inventory_items_cultivator_id_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_equipment_slots", + "columnsFrom": [ + "cultivator_id", + "equipment_instance_id" + ], + "tableTo": "wanjiedaoyou_inventory_items", + "columnsTo": [ + "cultivator_id", + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": { + "wanjiedaoyou_cultivator_equipment_slots_cultivator_id_slot_pk": { + "name": "wanjiedaoyou_cultivator_equipment_slots_cultivator_id_slot_pk", + "columns": [ + "cultivator_id", + "slot" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivator_friends": { + "name": "wanjiedaoyou_cultivator_friends", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "friend_cultivator_id": { + "name": "friend_cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "cultivator_friends_pair_uidx": { + "name": "cultivator_friends_pair_uidx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "friend_cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cultivator_friends_friend_idx": { + "name": "cultivator_friends_friend_idx", + "columns": [ + { + "expression": "friend_cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_cultivator_friends_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_friends_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_friends", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_cultivator_friends_friend_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_friends_friend_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_friends", + "columnsFrom": [ + "friend_cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivator_manual_slots": { + "name": "wanjiedaoyou_cultivator_manual_slots", + "schema": "", + "columns": { + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slot": { + "name": "slot", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "manual_id": { + "name": "manual_id", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "cultivator_manual_unique": { + "name": "cultivator_manual_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "manual_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_cultivator_manual_slots_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_manual_slots_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_manual_slots", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": { + "wanjiedaoyou_cultivator_manual_slots_cultivator_id_slot_pk": { + "name": "wanjiedaoyou_cultivator_manual_slots_cultivator_id_slot_pk", + "columns": [ + "cultivator_id", + "slot" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cultivator_manual_slot_valid": { + "name": "cultivator_manual_slot_valid", + "value": "\"wanjiedaoyou_cultivator_manual_slots\".\"slot\" BETWEEN 1 AND 4" + } + }, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivator_manual_states": { + "name": "wanjiedaoyou_cultivator_manual_states", + "schema": "", + "columns": { + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "learned": { + "name": "learned", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wanjiedaoyou_cultivator_manual_states_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_manual_states_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_manual_states", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "cultivator_manual_revision_valid": { + "name": "cultivator_manual_revision_valid", + "value": "\"wanjiedaoyou_cultivator_manual_states\".\"revision\" >= 0" + } + }, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivator_tasks": { + "name": "wanjiedaoyou_cultivator_tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "definition_id": { + "name": "definition_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "category": { + "name": "category", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "current_stage": { + "name": "current_stage", + "type": "varchar(120)", + "primaryKey": false, + "notNull": false + }, + "objectives": { + "name": "objectives", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "cultivator_tasks_cultivator_status_updated_idx": { + "name": "cultivator_tasks_cultivator_status_updated_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cultivator_tasks_cultivator_definition_unique": { + "name": "cultivator_tasks_cultivator_definition_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "definition_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_cultivator_tasks_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_cultivator_tasks_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_cultivator_tasks", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_cultivators": { + "name": "wanjiedaoyou_cultivators", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "gender": { + "name": "gender", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "origin": { + "name": "origin", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "personality": { + "name": "personality", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "background": { + "name": "background", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "player_race": { + "name": "player_race", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true, + "default": "'human'" + }, + "race_narrative": { + "name": "race_narrative", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "realm": { + "name": "realm", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "realm_stage": { + "name": "realm_stage", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "age": { + "name": "age", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 18 + }, + "lifespan": { + "name": "lifespan", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "closed_door_years_total": { + "name": "closed_door_years_total", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "died_at": { + "name": "died_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "vitality": { + "name": "vitality", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "strength": { + "name": "strength", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "spirit": { + "name": "spirit", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "endurance": { + "name": "endurance", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "speed": { + "name": "speed", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "willpower": { + "name": "willpower", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "unallocated_attribute_points": { + "name": "unallocated_attribute_points", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "spirit_stones": { + "name": "spirit_stones", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reputation": { + "name": "reputation", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "qi": { + "name": "qi", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 200 + }, + "qi_last_refreshed_at": { + "name": "qi_last_refreshed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_yield_at": { + "name": "last_yield_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "last_active_at": { + "name": "last_active_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "balance_notes": { + "name": "balance_notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "condition": { + "name": "condition", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "game_settings": { + "name": "game_settings", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "cultivation_progress": { + "name": "cultivation_progress", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "cultivators_user_status_updated_idx": { + "name": "cultivators_user_status_updated_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cultivators_status_created_idx": { + "name": "cultivators_status_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cultivators_name_idx": { + "name": "cultivators_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "cultivators_status_spirit_stones_idx": { + "name": "cultivators_status_spirit_stones_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "spirit_stones", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_dungeon_histories": { + "name": "wanjiedaoyou_dungeon_histories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "log": { + "name": "log", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "real_gains": { + "name": "real_gains", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dungeon_histories_cultivator_created_idx": { + "name": "dungeon_histories_cultivator_created_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_dungeon_histories_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_dungeon_histories_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_dungeon_histories", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_dungeon_runs": { + "name": "wanjiedaoyou_dungeon_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "map_node_id": { + "name": "map_node_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(30)", + "primaryKey": false, + "notNull": true, + "default": "'EXPLORING'" + }, + "current_round": { + "name": "current_round", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "max_rounds": { + "name": "max_rounds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "danger_score": { + "name": "danger_score", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 10 + }, + "run_state": { + "name": "run_state", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "cost_ledger": { + "name": "cost_ledger", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "gain_ledger": { + "name": "gain_ledger", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "pending_action": { + "name": "pending_action", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "active_battle_id": { + "name": "active_battle_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "battle_payload": { + "name": "battle_payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "dungeon_runs_cultivator_status_updated_idx": { + "name": "dungeon_runs_cultivator_status_updated_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "dungeon_runs_status_updated_idx": { + "name": "dungeon_runs_status_updated_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_dungeon_runs_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_dungeon_runs_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_dungeon_runs", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_feedbacks": { + "name": "wanjiedaoyou_feedbacks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "feedback_user_created_at_idx": { + "name": "feedback_user_created_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "feedback_status_type_created_at_idx": { + "name": "feedback_status_type_created_at_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_feedbacks_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_feedbacks_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_feedbacks", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_inventory_items": { + "name": "wanjiedaoyou_inventory_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "varchar(160)", + "primaryKey": true, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "location": { + "name": "location", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "slot_index": { + "name": "slot_index", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "definition_id": { + "name": "definition_id", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + }, + "stack_key": { + "name": "stack_key", + "type": "varchar(200)", + "primaryKey": false, + "notNull": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "instance_data": { + "name": "instance_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inventory_owner_id_unique": { + "name": "inventory_owner_id_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "inventory_owner_location_idx": { + "name": "inventory_owner_location_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "location", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "inventory_stack_lookup_idx": { + "name": "inventory_stack_lookup_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "location", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "definition_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stack_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "inventory_bag_slot_unique": { + "name": "inventory_bag_slot_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "slot_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"wanjiedaoyou_inventory_items\".\"location\" = 'bag'", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_inventory_items_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_inventory_items_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_inventory_items", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "inventory_quantity_positive": { + "name": "inventory_quantity_positive", + "value": "\"wanjiedaoyou_inventory_items\".\"quantity\" > 0 AND \"wanjiedaoyou_inventory_items\".\"revision\" >= 0" + }, + "inventory_slot_valid": { + "name": "inventory_slot_valid", + "value": "(\"wanjiedaoyou_inventory_items\".\"location\" = 'bag' AND \"wanjiedaoyou_inventory_items\".\"slot_index\" IS NOT NULL AND \"wanjiedaoyou_inventory_items\".\"slot_index\" >= 0) OR (\"wanjiedaoyou_inventory_items\".\"location\" = 'storage' AND \"wanjiedaoyou_inventory_items\".\"slot_index\" IS NULL) OR (\"wanjiedaoyou_inventory_items\".\"location\" = 'equipped' AND \"wanjiedaoyou_inventory_items\".\"slot_index\" IS NULL AND \"wanjiedaoyou_inventory_items\".\"definition_id\" = 'equipment.v6')" + } + }, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_item_library": { + "name": "wanjiedaoyou_item_library", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "item_id": { + "name": "item_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'published'" + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "quality": { + "name": "quality", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "element": { + "name": "element", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "varchar(40)", + "primaryKey": false, + "notNull": false + }, + "sample_key": { + "name": "sample_key", + "type": "double precision", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "editor_config": { + "name": "editor_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "item_library_item_id_unique": { + "name": "item_library_item_id_unique", + "columns": [ + { + "expression": "item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "item_library_status_type_idx": { + "name": "item_library_status_type_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "item_library_material_sample_idx": { + "name": "item_library_material_sample_idx", + "columns": [ + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "quality", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sample_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "item_library_name_idx": { + "name": "item_library_name_idx", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_mails": { + "name": "wanjiedaoyou_mails", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "attachments": { + "name": "attachments", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_read": { + "name": "is_read", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_claimed": { + "name": "is_claimed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "mails_cultivator_created_idx": { + "name": "mails_cultivator_created_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "mails_cultivator_is_read_created_idx": { + "name": "mails_cultivator_is_read_created_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "is_read", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_mails_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_mails_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_mails", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_materials": { + "name": "wanjiedaoyou_materials", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "rank": { + "name": "rank", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "element": { + "name": "element", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "materials_cultivator_idx": { + "name": "materials_cultivator_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "materials_cultivator_name_idx": { + "name": "materials_cultivator_name_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "materials_cultivator_name_rank_idx": { + "name": "materials_cultivator_name_rank_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "rank", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_materials_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_materials_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_materials", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_message_consumptions": { + "name": "wanjiedaoyou_message_consumptions", + "schema": "", + "columns": { + "consumer_name": { + "name": "consumer_name", + "type": "varchar(96)", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_key": { + "name": "message_key", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_consumptions_processed_idx": { + "name": "message_consumptions_processed_idx", + "columns": [ + { + "expression": "processed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": { + "wanjiedaoyou_message_consumptions_consumer_name_message_id_pk": { + "name": "wanjiedaoyou_message_consumptions_consumer_name_message_id_pk", + "columns": [ + "consumer_name", + "message_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_player_mutation_requests": { + "name": "wanjiedaoyou_player_mutation_requests", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(96)", + "primaryKey": false, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "request_fingerprint": { + "name": "request_fingerprint", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "player_mutation_requests_scope_unique": { + "name": "player_mutation_requests_scope_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "player_mutation_requests_created_idx": { + "name": "player_mutation_requests_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_player_mutation_requests_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_player_mutation_requests_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_player_mutation_requests", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_pre_heaven_fates": { + "name": "wanjiedaoyou_pre_heaven_fates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "quality": { + "name": "quality", + "type": "varchar(10)", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "pre_heaven_fates_cultivator_idx": { + "name": "pre_heaven_fates_cultivator_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_pre_heaven_fates_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_pre_heaven_fates_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_pre_heaven_fates", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_qi_logs": { + "name": "wanjiedaoyou_qi_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "action_instance_id": { + "name": "action_instance_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "qi_cost": { + "name": "qi_cost", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "qi_gain": { + "name": "qi_gain", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "qi_before": { + "name": "qi_before", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "qi_after": { + "name": "qi_after", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(64)", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "qi_logs_action_instance_uidx": { + "name": "qi_logs_action_instance_uidx", + "columns": [ + { + "expression": "action_instance_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "qi_logs_cultivator_created_idx": { + "name": "qi_logs_cultivator_created_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "qi_logs_status_created_idx": { + "name": "qi_logs_status_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_qi_logs_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_qi_logs_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_qi_logs", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_redeem_code_claims": { + "name": "wanjiedaoyou_redeem_code_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "redeem_code_id": { + "name": "redeem_code_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "mail_id": { + "name": "mail_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "redeem_code_claims_code_user_unique": { + "name": "redeem_code_claims_code_user_unique", + "columns": [ + { + "expression": "redeem_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "redeem_code_claims_user_idx": { + "name": "redeem_code_claims_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "redeem_code_claims_code_idx": { + "name": "redeem_code_claims_code_idx", + "columns": [ + { + "expression": "redeem_code_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_redeem_code_claims_redeem_code_id_wanjiedaoyou_redeem_codes_id_fk": { + "name": "wanjiedaoyou_redeem_code_claims_redeem_code_id_wanjiedaoyou_redeem_codes_id_fk", + "tableFrom": "wanjiedaoyou_redeem_code_claims", + "columnsFrom": [ + "redeem_code_id" + ], + "tableTo": "wanjiedaoyou_redeem_codes", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_redeem_code_claims_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_redeem_code_claims_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_redeem_code_claims", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_redeem_code_claims_mail_id_wanjiedaoyou_mails_id_fk": { + "name": "wanjiedaoyou_redeem_code_claims_mail_id_wanjiedaoyou_mails_id_fk", + "tableFrom": "wanjiedaoyou_redeem_code_claims", + "columnsFrom": [ + "mail_id" + ], + "tableTo": "wanjiedaoyou_mails", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_redeem_codes": { + "name": "wanjiedaoyou_redeem_codes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "code": { + "name": "code", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "reward_preset_id": { + "name": "reward_preset_id", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "reward_attachments": { + "name": "reward_attachments", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "mail_title": { + "name": "mail_title", + "type": "varchar(200)", + "primaryKey": false, + "notNull": true + }, + "mail_content": { + "name": "mail_content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "total_limit": { + "name": "total_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "claimed_count": { + "name": "claimed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "redeem_codes_code_unique": { + "name": "redeem_codes_code_unique", + "columns": [ + { + "expression": "code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "redeem_codes_status_created_idx": { + "name": "redeem_codes_status_created_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "redeem_codes_created_idx": { + "name": "redeem_codes_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_reputation_shop_items": { + "name": "wanjiedaoyou_reputation_shop_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "item_library_item_id": { + "name": "item_library_item_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "per_user_limit": { + "name": "per_user_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reputation_shop_item_library_item_uidx": { + "name": "reputation_shop_item_library_item_uidx", + "columns": [ + { + "expression": "item_library_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reputation_shop_status_sort_idx": { + "name": "reputation_shop_status_sort_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_reputation_shop_items_item_library_item_id_wanjiedaoyou_item_library_item_id_fk": { + "name": "wanjiedaoyou_reputation_shop_items_item_library_item_id_wanjiedaoyou_item_library_item_id_fk", + "tableFrom": "wanjiedaoyou_reputation_shop_items", + "columnsFrom": [ + "item_library_item_id" + ], + "tableTo": "wanjiedaoyou_item_library", + "columnsTo": [ + "item_id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_reputation_shop_purchases": { + "name": "wanjiedaoyou_reputation_shop_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "shop_item_id": { + "name": "shop_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "item_library_item_id": { + "name": "item_library_item_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "reputation_cost": { + "name": "reputation_cost", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "purchase_week": { + "name": "purchase_week", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reputation_shop_purchases_cultivator_item_idx": { + "name": "reputation_shop_purchases_cultivator_item_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shop_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reputation_shop_purchases_week_idx": { + "name": "reputation_shop_purchases_week_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shop_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "purchase_week", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "reputation_shop_purchases_created_idx": { + "name": "reputation_shop_purchases_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_reputation_shop_purchases_shop_item_id_wanjiedaoyou_reputation_shop_items_id_fk": { + "name": "wanjiedaoyou_reputation_shop_purchases_shop_item_id_wanjiedaoyou_reputation_shop_items_id_fk", + "tableFrom": "wanjiedaoyou_reputation_shop_purchases", + "columnsFrom": [ + "shop_item_id" + ], + "tableTo": "wanjiedaoyou_reputation_shop_items", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_reputation_shop_purchases_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_reputation_shop_purchases_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_reputation_shop_purchases", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_resource_events": { + "name": "wanjiedaoyou_resource_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_version": { + "name": "scope_version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "resource_version": { + "name": "resource_version", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "resource_key": { + "name": "resource_key", + "type": "varchar(96)", + "primaryKey": false, + "notNull": true + }, + "operation": { + "name": "operation", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true + }, + "event_type": { + "name": "event_type", + "type": "varchar(96)", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "actor_cultivator_id": { + "name": "actor_cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "varchar(96)", + "primaryKey": false, + "notNull": true + }, + "request_id": { + "name": "request_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "mutation_ordinal": { + "name": "mutation_ordinal", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "resource_events_scope_version_ordinal_unique": { + "name": "resource_events_scope_version_ordinal_unique", + "columns": [ + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mutation_ordinal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "resource_events_scope_version_idx": { + "name": "resource_events_scope_version_idx", + "columns": [ + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mutation_ordinal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "resource_events_replay_idx": { + "name": "resource_events_replay_idx", + "columns": [ + { + "expression": "actor_cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "request_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "mutation_ordinal", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "resource_events_created_idx": { + "name": "resource_events_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_resource_events_scope_id_wanjiedaoyou_resource_scopes_id_fk": { + "name": "wanjiedaoyou_resource_events_scope_id_wanjiedaoyou_resource_scopes_id_fk", + "tableFrom": "wanjiedaoyou_resource_events", + "columnsFrom": [ + "scope_id" + ], + "tableTo": "wanjiedaoyou_resource_scopes", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_resource_events_actor_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_resource_events_actor_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_resource_events", + "columnsFrom": [ + "actor_cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_resource_scopes": { + "name": "wanjiedaoyou_resource_scopes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scope_kind": { + "name": "scope_kind", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true + }, + "scope_key": { + "name": "scope_key", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "scope_version": { + "name": "scope_version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "resource_scopes_kind_key_unique": { + "name": "resource_scopes_kind_key_unique", + "columns": [ + { + "expression": "scope_kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_resource_versions": { + "name": "wanjiedaoyou_resource_versions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_key": { + "name": "resource_key", + "type": "varchar(96)", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "resource_versions_scope_key_unique": { + "name": "resource_versions_scope_key_unique", + "columns": [ + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "resource_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "resource_versions_resource_idx": { + "name": "resource_versions_resource_idx", + "columns": [ + { + "expression": "resource_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_resource_versions_scope_id_wanjiedaoyou_resource_scopes_id_fk": { + "name": "wanjiedaoyou_resource_versions_scope_id_wanjiedaoyou_resource_scopes_id_fk", + "tableFrom": "wanjiedaoyou_resource_versions", + "columnsFrom": [ + "scope_id" + ], + "tableTo": "wanjiedaoyou_resource_scopes", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_retreat_records": { + "name": "wanjiedaoyou_retreat_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "realm": { + "name": "realm", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "realm_stage": { + "name": "realm_stage", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "years": { + "name": "years", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "success": { + "name": "success", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "chance": { + "name": "chance", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "roll": { + "name": "roll", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "modifiers": { + "name": "modifiers", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "retreat_records_cultivator_timestamp_idx": { + "name": "retreat_records_cultivator_timestamp_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "timestamp", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_retreat_records_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_retreat_records_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_retreat_records", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_combat_states": { + "name": "wanjiedaoyou_sect_combat_states", + "schema": "", + "columns": { + "membership_id": { + "name": "membership_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "active_path_id": { + "name": "active_path_id", + "type": "varchar(160)", + "primaryKey": false, + "notNull": false + }, + "meridian_depth": { + "name": "meridian_depth", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "wanjiedaoyou_sect_combat_states_membership_id_wanjiedaoyou_sect_memberships_id_fk": { + "name": "wanjiedaoyou_sect_combat_states_membership_id_wanjiedaoyou_sect_memberships_id_fk", + "tableFrom": "wanjiedaoyou_sect_combat_states", + "columnsFrom": [ + "membership_id" + ], + "tableTo": "wanjiedaoyou_sect_memberships", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "sect_combat_progress_valid": { + "name": "sect_combat_progress_valid", + "value": "\"wanjiedaoyou_sect_combat_states\".\"revision\" >= 0 AND \"wanjiedaoyou_sect_combat_states\".\"meridian_depth\" BETWEEN 0 AND 7" + } + }, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_facilities": { + "name": "wanjiedaoyou_sect_facilities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sect_id": { + "name": "sect_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "facility_key": { + "name": "facility_key", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "progress": { + "name": "progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sect_facilities_sect_key_unique": { + "name": "sect_facilities_sect_key_unique", + "columns": [ + { + "expression": "sect_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "facility_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_memberships": { + "name": "wanjiedaoyou_sect_memberships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sect_id": { + "name": "sect_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'prospect'" + }, + "experienced_at": { + "name": "experienced_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "joined_at": { + "name": "joined_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "contribution": { + "name": "contribution", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "lifetime_contribution": { + "name": "lifetime_contribution", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "disciple_rank": { + "name": "disciple_rank", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'registered'" + }, + "office": { + "name": "office", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "promoted_at": { + "name": "promoted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "config_version": { + "name": "config_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 2 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sect_memberships_cultivator_sect_unique": { + "name": "sect_memberships_cultivator_sect_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sect_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sect_memberships_active_cultivator_unique": { + "name": "sect_memberships_active_cultivator_unique", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"wanjiedaoyou_sect_memberships\".\"status\" = 'active'", + "concurrently": false + }, + "sect_memberships_sect_status_idx": { + "name": "sect_memberships_sect_status_idx", + "columns": [ + { + "expression": "sect_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_memberships_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_sect_memberships_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_sect_memberships", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_meridian_loadouts": { + "name": "wanjiedaoyou_sect_meridian_loadouts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "membership_id": { + "name": "membership_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "path_id": { + "name": "path_id", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + }, + "revision": { + "name": "revision", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "sect_meridian_membership_path_unique": { + "name": "sect_meridian_membership_path_unique", + "columns": [ + { + "expression": "membership_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "path_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_meridian_loadouts_membership_id_wanjiedaoyou_sect_memberships_id_fk": { + "name": "wanjiedaoyou_sect_meridian_loadouts_membership_id_wanjiedaoyou_sect_memberships_id_fk", + "tableFrom": "wanjiedaoyou_sect_meridian_loadouts", + "columnsFrom": [ + "membership_id" + ], + "tableTo": "wanjiedaoyou_sect_memberships", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_meridian_nodes": { + "name": "wanjiedaoyou_sect_meridian_nodes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "loadout_id": { + "name": "loadout_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "node_id": { + "name": "node_id", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + }, + "layer": { + "name": "layer", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "sect_meridian_loadout_node_unique": { + "name": "sect_meridian_loadout_node_unique", + "columns": [ + { + "expression": "loadout_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "node_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sect_meridian_loadout_layer_unique": { + "name": "sect_meridian_loadout_layer_unique", + "columns": [ + { + "expression": "loadout_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "layer", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_meridian_nodes_loadout_id_wanjiedaoyou_sect_meridian_loadouts_id_fk": { + "name": "wanjiedaoyou_sect_meridian_nodes_loadout_id_wanjiedaoyou_sect_meridian_loadouts_id_fk", + "tableFrom": "wanjiedaoyou_sect_meridian_nodes", + "columnsFrom": [ + "loadout_id" + ], + "tableTo": "wanjiedaoyou_sect_meridian_loadouts", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_method_progress": { + "name": "wanjiedaoyou_sect_method_progress", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "membership_id": { + "name": "membership_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "method_id": { + "name": "method_id", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": { + "sect_method_membership_method_unique": { + "name": "sect_method_membership_method_unique", + "columns": [ + { + "expression": "membership_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "method_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_method_progress_membership_id_wanjiedaoyou_sect_memberships_id_fk": { + "name": "wanjiedaoyou_sect_method_progress_membership_id_wanjiedaoyou_sect_memberships_id_fk", + "tableFrom": "wanjiedaoyou_sect_method_progress", + "columnsFrom": [ + "membership_id" + ], + "tableTo": "wanjiedaoyou_sect_memberships", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_shop_items": { + "name": "wanjiedaoyou_sect_shop_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "item_library_item_id": { + "name": "item_library_item_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "price": { + "name": "price", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "per_user_limit": { + "name": "per_user_limit", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sect_shop_item_library_item_uidx": { + "name": "sect_shop_item_library_item_uidx", + "columns": [ + { + "expression": "item_library_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sect_shop_status_sort_idx": { + "name": "sect_shop_status_sort_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_shop_items_item_library_item_id_wanjiedaoyou_item_library_item_id_fk": { + "name": "wanjiedaoyou_sect_shop_items_item_library_item_id_wanjiedaoyou_item_library_item_id_fk", + "tableFrom": "wanjiedaoyou_sect_shop_items", + "columnsFrom": [ + "item_library_item_id" + ], + "tableTo": "wanjiedaoyou_item_library", + "columnsTo": [ + "item_id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_shop_purchases": { + "name": "wanjiedaoyou_sect_shop_purchases", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "shop_item_id": { + "name": "shop_item_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "membership_id": { + "name": "membership_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "item_library_item_id": { + "name": "item_library_item_id", + "type": "varchar(120)", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "contribution_cost": { + "name": "contribution_cost", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "purchase_week": { + "name": "purchase_week", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sect_shop_purchases_cultivator_item_idx": { + "name": "sect_shop_purchases_cultivator_item_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shop_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sect_shop_purchases_week_idx": { + "name": "sect_shop_purchases_week_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shop_item_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "purchase_week", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sect_shop_purchases_created_idx": { + "name": "sect_shop_purchases_created_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_shop_purchases_shop_item_id_wanjiedaoyou_sect_shop_items_id_fk": { + "name": "wanjiedaoyou_sect_shop_purchases_shop_item_id_wanjiedaoyou_sect_shop_items_id_fk", + "tableFrom": "wanjiedaoyou_sect_shop_purchases", + "columnsFrom": [ + "shop_item_id" + ], + "tableTo": "wanjiedaoyou_sect_shop_items", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_sect_shop_purchases_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_sect_shop_purchases_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_sect_shop_purchases", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_sect_shop_purchases_membership_id_wanjiedaoyou_sect_memberships_id_fk": { + "name": "wanjiedaoyou_sect_shop_purchases_membership_id_wanjiedaoyou_sect_memberships_id_fk", + "tableFrom": "wanjiedaoyou_sect_shop_purchases", + "columnsFrom": [ + "membership_id" + ], + "tableTo": "wanjiedaoyou_sect_memberships", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_stipend_claims": { + "name": "wanjiedaoyou_sect_stipend_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "membership_id": { + "name": "membership_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "week_key": { + "name": "week_key", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "spirit_stones": { + "name": "spirit_stones", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sect_stipend_member_week_unique": { + "name": "sect_stipend_member_week_unique", + "columns": [ + { + "expression": "membership_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "week_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sect_stipend_claimed_idx": { + "name": "sect_stipend_claimed_idx", + "columns": [ + { + "expression": "claimed_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_stipend_claims_membership_id_wanjiedaoyou_sect_memberships_id_fk": { + "name": "wanjiedaoyou_sect_stipend_claims_membership_id_wanjiedaoyou_sect_memberships_id_fk", + "tableFrom": "wanjiedaoyou_sect_stipend_claims", + "columnsFrom": [ + "membership_id" + ], + "tableTo": "wanjiedaoyou_sect_memberships", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sect_task_records": { + "name": "wanjiedaoyou_sect_task_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "membership_id": { + "name": "membership_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "period_key": { + "name": "period_key", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "attempt": { + "name": "attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "progress": { + "name": "progress", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sect_task_membership_period_task_attempt_unique": { + "name": "sect_task_membership_period_task_attempt_unique", + "columns": [ + { + "expression": "membership_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "task_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "attempt", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sect_task_membership_kind_period_idx": { + "name": "sect_task_membership_kind_period_idx", + "columns": [ + { + "expression": "membership_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sect_task_records_membership_id_wanjiedaoyou_sect_memberships_id_fk": { + "name": "wanjiedaoyou_sect_task_records_membership_id_wanjiedaoyou_sect_memberships_id_fk", + "tableFrom": "wanjiedaoyou_sect_task_records", + "columnsFrom": [ + "membership_id" + ], + "tableTo": "wanjiedaoyou_sect_memberships", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_spirit_fields": { + "name": "wanjiedaoyou_spirit_fields", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "self_harvest_count": { + "name": "self_harvest_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_care_count": { + "name": "total_care_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "starter_claimed": { + "name": "starter_claimed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "plots": { + "name": "plots", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "spirit_fields_cultivator_uidx": { + "name": "spirit_fields_cultivator_uidx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "spirit_fields_updated_idx": { + "name": "spirit_fields_updated_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_spirit_fields_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_spirit_fields_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_spirit_fields", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_spiritual_roots": { + "name": "wanjiedaoyou_spiritual_roots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "element": { + "name": "element", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "strength": { + "name": "strength", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "marrow_wash_bonus": { + "name": "marrow_wash_bonus", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "grade": { + "name": "grade", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "spiritual_roots_cultivator_idx": { + "name": "spiritual_roots_cultivator_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_spiritual_roots_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_spiritual_roots_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_spiritual_roots", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sponsorship_admin_actions": { + "name": "wanjiedaoyou_sponsorship_admin_actions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "admin_user_id": { + "name": "admin_user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "order_id": { + "name": "order_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sponsorship_admin_actions_admin_created_idx": { + "name": "sponsorship_admin_actions_admin_created_idx", + "columns": [ + { + "expression": "admin_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_admin_actions_order_created_idx": { + "name": "sponsorship_admin_actions_order_created_idx", + "columns": [ + { + "expression": "order_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sponsorship_admin_actions_order_id_wanjiedaoyou_sponsorship_orders_id_fk": { + "name": "wanjiedaoyou_sponsorship_admin_actions_order_id_wanjiedaoyou_sponsorship_orders_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_admin_actions", + "columnsFrom": [ + "order_id" + ], + "tableTo": "wanjiedaoyou_sponsorship_orders", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sponsorship_checkout_intents": { + "name": "wanjiedaoyou_sponsorship_checkout_intents", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "tier": { + "name": "tier", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "expected_plan_id": { + "name": "expected_plan_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "public_listing": { + "name": "public_listing", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "status": { + "name": "status", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "provider_order_id": { + "name": "provider_order_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "config_snapshot": { + "name": "config_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sponsorship_checkout_provider_order_uidx": { + "name": "sponsorship_checkout_provider_order_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_order_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_checkout_user_created_idx": { + "name": "sponsorship_checkout_user_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_checkout_status_expires_idx": { + "name": "sponsorship_checkout_status_expires_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sponsorship_checkout_intents_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_sponsorship_checkout_intents_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_checkout_intents", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sponsorship_claims": { + "name": "wanjiedaoyou_sponsorship_claims", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "order_id": { + "name": "order_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "code_hash": { + "name": "code_hash", + "type": "varchar(64)", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_listing": { + "name": "public_listing", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "status": { + "name": "status", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "claimed_at": { + "name": "claimed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "message_status": { + "name": "message_status", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message_attempts": { + "name": "message_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_message_error": { + "name": "last_message_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sponsorship_claims_order_uidx": { + "name": "sponsorship_claims_order_uidx", + "columns": [ + { + "expression": "order_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_claims_code_hash_uidx": { + "name": "sponsorship_claims_code_hash_uidx", + "columns": [ + { + "expression": "code_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_claims_status_expires_idx": { + "name": "sponsorship_claims_status_expires_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sponsorship_claims_order_id_wanjiedaoyou_sponsorship_orders_id_fk": { + "name": "wanjiedaoyou_sponsorship_claims_order_id_wanjiedaoyou_sponsorship_orders_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_claims", + "columnsFrom": [ + "order_id" + ], + "tableTo": "wanjiedaoyou_sponsorship_orders", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_sponsorship_claims_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_sponsorship_claims_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_claims", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sponsorship_merit_profiles": { + "name": "wanjiedaoyou_sponsorship_merit_profiles", + "schema": "", + "columns": { + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "highest_tier": { + "name": "highest_tier", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "merit_count": { + "name": "merit_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "first_supported_at": { + "name": "first_supported_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "last_supported_at": { + "name": "last_supported_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sponsorship_merit_public_tier_first_idx": { + "name": "sponsorship_merit_public_tier_first_idx", + "columns": [ + { + "expression": "is_public", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "highest_tier", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "first_supported_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sponsorship_merit_profiles_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_sponsorship_merit_profiles_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_merit_profiles", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sponsorship_merit_records": { + "name": "wanjiedaoyou_sponsorship_merit_records", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "order_id": { + "name": "order_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "cultivator_id": { + "name": "cultivator_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tier": { + "name": "tier", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "supported_at": { + "name": "supported_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "mail_id": { + "name": "mail_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sponsorship_merit_order_uidx": { + "name": "sponsorship_merit_order_uidx", + "columns": [ + { + "expression": "order_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_merit_cultivator_supported_idx": { + "name": "sponsorship_merit_cultivator_supported_idx", + "columns": [ + { + "expression": "cultivator_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "supported_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sponsorship_merit_records_order_id_wanjiedaoyou_sponsorship_orders_id_fk": { + "name": "wanjiedaoyou_sponsorship_merit_records_order_id_wanjiedaoyou_sponsorship_orders_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_merit_records", + "columnsFrom": [ + "order_id" + ], + "tableTo": "wanjiedaoyou_sponsorship_orders", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "wanjiedaoyou_sponsorship_merit_records_cultivator_id_wanjiedaoyou_cultivators_id_fk": { + "name": "wanjiedaoyou_sponsorship_merit_records_cultivator_id_wanjiedaoyou_cultivators_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_merit_records", + "columnsFrom": [ + "cultivator_id" + ], + "tableTo": "wanjiedaoyou_cultivators", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "wanjiedaoyou_sponsorship_merit_records_mail_id_wanjiedaoyou_mails_id_fk": { + "name": "wanjiedaoyou_sponsorship_merit_records_mail_id_wanjiedaoyou_mails_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_merit_records", + "columnsFrom": [ + "mail_id" + ], + "tableTo": "wanjiedaoyou_mails", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sponsorship_order_snapshots": { + "name": "wanjiedaoyou_sponsorship_order_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "order_id": { + "name": "order_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "purge_after": { + "name": "purge_after", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sponsorship_snapshots_order_created_idx": { + "name": "sponsorship_snapshots_order_created_idx", + "columns": [ + { + "expression": "order_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_snapshots_purge_idx": { + "name": "sponsorship_snapshots_purge_idx", + "columns": [ + { + "expression": "purge_after", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sponsorship_order_snapshots_order_id_wanjiedaoyou_sponsorship_orders_id_fk": { + "name": "wanjiedaoyou_sponsorship_order_snapshots_order_id_wanjiedaoyou_sponsorship_orders_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_order_snapshots", + "columnsFrom": [ + "order_id" + ], + "tableTo": "wanjiedaoyou_sponsorship_orders", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_sponsorship_orders": { + "name": "wanjiedaoyou_sponsorship_orders", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "provider": { + "name": "provider", + "type": "varchar(32)", + "primaryKey": false, + "notNull": true + }, + "provider_order_id": { + "name": "provider_order_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": true + }, + "custom_order_id": { + "name": "custom_order_id", + "type": "varchar(128)", + "primaryKey": false, + "notNull": false + }, + "provider_user_id": { + "name": "provider_user_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "plan_id": { + "name": "plan_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "sku_id": { + "name": "sku_id", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "product_type": { + "name": "product_type", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_amount_fen": { + "name": "total_amount_fen", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "show_amount_fen": { + "name": "show_amount_fen", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "month": { + "name": "month", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "provider_status": { + "name": "provider_status", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "provider_created_at": { + "name": "provider_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "verification_status": { + "name": "verification_status", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true, + "default": "'received'" + }, + "fulfillment_status": { + "name": "fulfillment_status", + "type": "varchar(24)", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "resolved_tier": { + "name": "resolved_tier", + "type": "varchar(32)", + "primaryKey": false, + "notNull": false + }, + "checkout_intent_id": { + "name": "checkout_intent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "config_snapshot": { + "name": "config_snapshot", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "retry_count": { + "name": "retry_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error_code": { + "name": "last_error_code", + "type": "varchar(80)", + "primaryKey": false, + "notNull": false + }, + "last_error_message": { + "name": "last_error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "signature_verified_at": { + "name": "signature_verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "verified_at": { + "name": "verified_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "fulfilled_at": { + "name": "fulfilled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "sensitive_purged_at": { + "name": "sensitive_purged_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sponsorship_orders_provider_order_uidx": { + "name": "sponsorship_orders_provider_order_uidx", + "columns": [ + { + "expression": "provider", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "provider_order_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_orders_verification_created_idx": { + "name": "sponsorship_orders_verification_created_idx", + "columns": [ + { + "expression": "verification_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_orders_fulfillment_created_idx": { + "name": "sponsorship_orders_fulfillment_created_idx", + "columns": [ + { + "expression": "fulfillment_status", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "sponsorship_orders_provider_user_idx": { + "name": "sponsorship_orders_provider_user_idx", + "columns": [ + { + "expression": "provider_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": { + "wanjiedaoyou_sponsorship_orders_checkout_intent_id_wanjiedaoyou_sponsorship_checkout_intents_id_fk": { + "name": "wanjiedaoyou_sponsorship_orders_checkout_intent_id_wanjiedaoyou_sponsorship_checkout_intents_id_fk", + "tableFrom": "wanjiedaoyou_sponsorship_orders", + "columnsFrom": [ + "checkout_intent_id" + ], + "tableTo": "wanjiedaoyou_sponsorship_checkout_intents", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_tower_enemy_floors": { + "name": "wanjiedaoyou_tower_enemy_floors", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_key": { + "name": "season_key", + "type": "varchar(40)", + "primaryKey": false, + "notNull": true + }, + "realm": { + "name": "realm", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "floor": { + "name": "floor", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true, + "default": "'ready'" + }, + "schema_version": { + "name": "schema_version", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "enemy": { + "name": "enemy", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "generated_at": { + "name": "generated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tower_enemy_floors_season_realm_floor_uidx": { + "name": "tower_enemy_floors_season_realm_floor_uidx", + "columns": [ + { + "expression": "season_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "realm", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "floor", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "concurrently": false + }, + "tower_enemy_floors_realm_floor_generated_idx": { + "name": "tower_enemy_floors_realm_floor_generated_idx", + "columns": [ + { + "expression": "realm", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "floor", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "generated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + }, + "tower_enemy_floors_season_realm_status_idx": { + "name": "tower_enemy_floors_season_realm_status_idx", + "columns": [ + { + "expression": "season_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "realm", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wanjiedaoyou_transactional_messages": { + "name": "wanjiedaoyou_transactional_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "message_key": { + "name": "message_key", + "type": "varchar(128)", + "primaryKey": false, + "notNull": true + }, + "destination": { + "name": "destination", + "type": "varchar(160)", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "deduplication_key": { + "name": "deduplication_key", + "type": "varchar(256)", + "primaryKey": false, + "notNull": false + }, + "published_at": { + "name": "published_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "publish_attempts": { + "name": "publish_attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_publish_error": { + "name": "last_publish_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "transactional_messages_dedupe_unique": { + "name": "transactional_messages_dedupe_unique", + "columns": [ + { + "expression": "message_key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "deduplication_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "with": {}, + "method": "btree", + "where": "\"wanjiedaoyou_transactional_messages\".\"deduplication_key\" is not null", + "concurrently": false + }, + "transactional_messages_pending_idx": { + "name": "transactional_messages_pending_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "with": {}, + "method": "btree", + "where": "\"wanjiedaoyou_transactional_messages\".\"published_at\" is null", + "concurrently": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index f206c26c..48053698 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -344,6 +344,13 @@ "when": 1789465138319, "tag": "0048_competitive_replay_retention", "breakpoints": true + }, + { + "idx": 49, + "version": "7", + "when": 1789533721588, + "tag": "0049_manual_first_release", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/react-app/components/feature/items/presentation/basic.ts b/src/react-app/components/feature/items/presentation/basic.ts index d49e4833..ec97739f 100644 --- a/src/react-app/components/feature/items/presentation/basic.ts +++ b/src/react-app/components/feature/items/presentation/basic.ts @@ -7,6 +7,7 @@ import { } from '@shared/engine/combat-v6/beasts/content'; import { BEAST_REFINEMENT } from '@shared/engine/combat-v6/beasts/refinement-config'; import { CHARACTER_MANUALS_V1 } from '@shared/engine/combat-v6/manuals/content'; +import { manualEffectLines } from '@shared/engine/combat-v6/manuals/presentation'; import { EQUIPMENT_SLOT_NAMES } from '@shared/items/definitions/equipment-blueprints'; import { MATERIAL_TYPE_NAMES } from '@shared/items/definitions/materials'; import { SeedFactsSchema } from '@shared/items/definitions/seeds'; @@ -116,7 +117,18 @@ export const manualAdapter: ItemAdapter = (item, def) => { field('传承境界', manual.realm), quantity(item, options), ], - sections: [{ title: '所载功法', entries: lines(manual.description) }], + sections: [ + { + title: '所载功法', + entries: lines( + [ + ...manualEffectLines(manual, 1), + '九层:' + manualEffectLines(manual, 9).join(';'), + manual.description, + ].join('\n'), + ), + }, + ], description: '封存功法传承的玉简,可于悟道室参悟其中法门。', }), }; diff --git a/src/react-app/components/feature/manuals/ManualJadePicker.tsx b/src/react-app/components/feature/manuals/ManualJadePicker.tsx index 3d15bbc7..4b5a7e32 100644 --- a/src/react-app/components/feature/manuals/ManualJadePicker.tsx +++ b/src/react-app/components/feature/manuals/ManualJadePicker.tsx @@ -9,7 +9,7 @@ import { manualSlot } from '@shared/engine/combat-v6/manuals/compiler'; import { CHARACTER_MANUALS_V1 } from '@shared/engine/combat-v6/manuals/content'; import type { CharacterManualDefV1 } from '@shared/engine/combat-v6/manuals/types'; import { itemDefinition } from '@shared/inventory'; -import { previewManualAction } from '@shared/manuals/action'; +import { manualJadeCost, previewManualAction } from '@shared/manuals/action'; import { InventoryItems } from '../items/InventoryItems'; export function ManualJadePicker({ @@ -25,6 +25,10 @@ export function ManualJadePicker({ disabled: boolean; onChoose: (action: ManualAction) => void; }) { + const jadeCost = + manualId && view.state + ? manualJadeCost(view.state, { action: 'unlock', manualId }) + : 1; const bag = useInventoryBag(); const items = bag.data?.items ?? []; const choices = items.flatMap((item) => { @@ -68,7 +72,7 @@ export function ManualJadePicker({ />

{manualId - ? '选择一本同名玉简,突破当前瓶颈。' + ? `选择同名玉简,本次突破消耗 ${jadeCost} 本。` : `选择一本${realm}功法玉简,开始修习。`}

{bag.error ? ( @@ -79,7 +83,7 @@ export function ManualJadePicker({ {bag.data && !bag.error && choices.length === 0 ? (

{manualId - ? '物品栏中暂无可用于突破的同名玉简。' + ? `物品栏中暂无数量足够的同名玉简,本次需要 ${jadeCost} 本。` : `物品栏中暂无可学习的${realm}功法玉简。`}

) : null} diff --git a/src/react-app/components/feature/manuals/ManualRealmSlot.tsx b/src/react-app/components/feature/manuals/ManualRealmSlot.tsx index d4e5751a..748d26d2 100644 --- a/src/react-app/components/feature/manuals/ManualRealmSlot.tsx +++ b/src/react-app/components/feature/manuals/ManualRealmSlot.tsx @@ -1,3 +1,4 @@ +import { manualAttributeValue } from '@shared/engine/combat-v6/manuals/attributes'; import { MAX_MANUALS_PER_SLOT } from '@shared/engine/combat-v6/manuals/compiler'; import { manualRule } from '@shared/engine/combat-v6/manuals/content'; import type { @@ -146,7 +147,6 @@ export function ManualRealmSlot({ {current.name} {progress?.level} 层 - {current.rarity === 'rare' ? ' · 珍稀' : ''} {manuals.length > 1 ? ( @@ -166,7 +166,7 @@ export function ManualRealmSlot({ {CHARACTER_ATTRIBUTE_LABELS[effect.attribute]}{' '} - +{effect.valuePerLevel * (progress?.level ?? 1)} + +{manualAttributeValue(effect, progress?.level ?? 1)} ))} diff --git a/src/react-app/components/feature/manuals/ManualRoom.tsx b/src/react-app/components/feature/manuals/ManualRoom.tsx index 853f7a54..3f65dc10 100644 --- a/src/react-app/components/feature/manuals/ManualRoom.tsx +++ b/src/react-app/components/feature/manuals/ManualRoom.tsx @@ -18,10 +18,10 @@ import { manualRule, } from '@shared/engine/combat-v6/manuals/content'; import { MANUAL_REALMS } from '@shared/engine/combat-v6/manuals/pack'; +import { manualEffectLines } from '@shared/engine/combat-v6/manuals/presentation'; import type { CharacterManualDefV1 } from '@shared/engine/combat-v6/manuals/types'; import { itemDefinition } from '@shared/inventory'; -import { CHARACTER_ATTRIBUTE_LABELS } from '@shared/lib/characterAttributeLabels'; -import { previewManualAction } from '@shared/manuals/action'; +import { manualJadeCost, previewManualAction } from '@shared/manuals/action'; import { useEffect, useRef, useState } from 'react'; import { useSearchParams } from 'react-router'; import { combatV6Request, mutationBody } from '../combat-v6/request'; @@ -30,14 +30,7 @@ import { ManualRealmSlot } from './ManualRealmSlot'; const endpoint = '/api/combat-v6/manuals'; function effects(manual: CharacterManualDefV1, level: number) { - return manual.effects - .map( - (e) => - CHARACTER_ATTRIBUTE_LABELS[e.attribute] + - ' +' + - e.valuePerLevel * level, - ) - .join(' · '); + return manualEffectLines(manual, level).join(';'); } export function ManualRoom() { @@ -323,15 +316,13 @@ export function ManualRoom() {

{manual.description}

- {manual.realm} · {manual.rarity === 'rare' ? '珍稀' : '常见'} ·{' '} + {manual.realm} ·{' '} {learned ? learned.level + '/' + rule.maxLevel + '层' : '未习得'}

-

{effects(manual, learned?.level ?? 1)}

+

{effects(manual, learned?.level ?? 1)}

{learned ? '圆满效果:' : '一层即可获得上述效果;圆满效果:'} - - {effects(manual, rule.maxLevel)} - + {effects(manual, rule.maxLevel)}

持有同名玉简{' '} @@ -362,7 +353,7 @@ export function ManualRoom() { {learnedInRealm}/{MAX_MANUALS_PER_SLOT} {' '} - 种。最多学习三种,暂不支持遗忘或替换,请谨慎选择。 + 种。最多收藏六种,同位仅一本生效,可免费切换。

) : null} {learned?.level === rule.maxLevel ? ( @@ -370,7 +361,16 @@ export function ManualRoom() { ) : ( <> {nextKind === 'unlock' ? ( -

已遇瓶颈,需一本同名玉简解锁后续层数。

+

+ 已遇瓶颈,需{' '} + + {manualJadeCost(view.state!, { + action: 'unlock', + manualId: manual.id, + })} + {' '} + 本同名玉简解锁后续层数。 +

) : null} {!confirmation ? ( = MAX_MANUALS_PER_SLOT ? ( -

该境界位已学满三种功法,不能继续学习新的功法。

+

该境界位已学满六种功法,不能继续学习新的功法。

) : null} {nextKind === 'train' && !preview?.ok ? (

{preview?.diagnostics.map((d) => d.message).join(';') ?? - '需要一本同名玉简'} + '暂时无法修炼,请刷新核对'}

) : null} @@ -466,7 +466,11 @@ export function ManualRoom() { ) : (

- 消耗一本《{manual.name}》玉简 + 消耗{' '} + + {manualJadeCost(view.state!, confirmation)} + {' '} + 本《{manual.name}》玉简 {confirmation.action === 'unlock' ? ',解锁后仍需修炼升层' : ''} diff --git a/src/server/lib/services/combat-v6/CombatV6ManualService.ts b/src/server/lib/services/combat-v6/CombatV6ManualService.ts index d32a84cd..6d370c9e 100644 --- a/src/server/lib/services/combat-v6/CombatV6ManualService.ts +++ b/src/server/lib/services/combat-v6/CombatV6ManualService.ts @@ -20,7 +20,7 @@ import type { ManualAction, ManualView, } from '@shared/contracts/combatV6Manuals'; -import { previewManualAction } from '@shared/manuals/action'; +import { manualJadeCost, previewManualAction } from '@shared/manuals/action'; import type { RealmStage, RealmType } from '@shared/types/constants'; import type { CultivationProgress } from '@shared/types/cultivator'; import { and, eq } from 'drizzle-orm'; @@ -156,15 +156,16 @@ export async function mutateManuals(owner: string, action: ManualAction) { .insert(cultivatorManualSlots) .values({ cultivatorId: owner, ...nextSlot }); if ('item' in action) { + const jadeCost = manualJadeCost(manuals, action); await saveInventoryPlan( owner, before, - item.quantity === 1 + item.quantity === jadeCost ? [] : [ { ...item, - quantity: item.quantity - 1, + quantity: item.quantity - jadeCost, revision: item.revision + 1, }, ], diff --git a/src/shared/combat-v6/public-build.test.ts b/src/shared/combat-v6/public-build.test.ts index d12e6650..3588d563 100644 --- a/src/shared/combat-v6/public-build.test.ts +++ b/src/shared/combat-v6/public-build.test.ts @@ -67,13 +67,13 @@ describe('public V6 build', () => { const baseline = publicCombatV6Build(character, input); input.manuals.learned = [ { manualId: 'character_manual.changchun', level: 3, unlockedLevel: 3 }, - { manualId: 'character_manual.songhe', level: 9, unlockedLevel: 9 }, + { manualId: 'character_manual.qingmu', level: 9, unlockedLevel: 9 }, ]; input.manuals.build.slots = [{ slot: 1, manualId: 'character_manual.changchun' }]; const result = publicCombatV6Build(character, input); expect(result.build.manuals).toEqual([{ slot: 1, manualId: 'character_manual.changchun', level: 3 }]); - expect(JSON.stringify(result.build)).not.toContain('songhe'); - expect(result.combatPanel.maxHp - baseline.combatPanel.maxHp).toBe(24); + expect(JSON.stringify(result.build)).not.toContain('qingmu'); + expect(result.combatPanel.maxHp - baseline.combatPanel.maxHp).toBe(128); expect(character.attributes.vitality).toBe(20); }); }); diff --git a/src/shared/engine/combat-v6/content/jiujie-pack.ts b/src/shared/engine/combat-v6/content/jiujie-pack.ts index 352bfe20..5ba358c1 100644 --- a/src/shared/engine/combat-v6/content/jiujie-pack.ts +++ b/src/shared/engine/combat-v6/content/jiujie-pack.ts @@ -84,7 +84,7 @@ export function compileJiujieCombat(pack: ReturnType) { function compileEffect(effect: z.infer): SkillEffect[] { if (effect.type === 'fiveThunderBranches') return pack.judgment.branches.map(branch => ({ type: EffectType.RandomBranch, branchId: branch.branchId, chance: branch.chance, when: branch.when, - successEffects: structuredClone(pack.judgment.successEffects), failureEffects: structuredClone(pack.judgment.failureEffects), + successEffects: pack.judgment.successEffects.map(e => e.type === EffectType.FixedHit ? { ...e, percentageDamage: true } : structuredClone(e)), failureEffects: pack.judgment.failureEffects.map(e => e.type === EffectType.FixedHit ? { ...e, percentageDamage: true } : structuredClone(e)), })); if (effect.type === 'detonation') { const rule = pack.detonation; diff --git a/src/shared/engine/combat-v6/content/skill-learning.test.ts b/src/shared/engine/combat-v6/content/skill-learning.test.ts index 5fe1766a..a4746b7c 100644 --- a/src/shared/engine/combat-v6/content/skill-learning.test.ts +++ b/src/shared/engine/combat-v6/content/skill-learning.test.ts @@ -9,8 +9,8 @@ import { compileSectDefinitionV6 } from './compiler'; import { createEmptySectCombatProgressV6, createFreshCombatV6MethodLevels } from '../build-state'; describe('宗门技能学习配置', () => { - it('所有技能、状态、流派节点和心法完整定义与迁移前一致', () => { - expect(createHash('sha256').update(JSON.stringify(COMBAT_V6_SECT_DEFINITIONS)).digest('hex')).toBe('e76aa73d14ab25af362a3a41140607242f80f6d0601c0382755cb16e01287a5c'); + it('技能、状态、流派和心法保持基线(含比例伤害分类)', () => { + expect(createHash('sha256').update(JSON.stringify(COMBAT_V6_SECT_DEFINITIONS)).digest('hex')).toBe('0773c048fc7c062f41d00ba6dbdfd42efaefd521d96b3046cf49addd17424565'); }); it('Schema 与编辑器一致', () => { expect(z.toJSONSchema(SectSkillLearningShape)).toEqual(schema); diff --git a/src/shared/engine/combat-v6/core/damage.ts b/src/shared/engine/combat-v6/core/damage.ts index 877c05d2..6a873482 100644 --- a/src/shared/engine/combat-v6/core/damage.ts +++ b/src/shared/engine/combat-v6/core/damage.ts @@ -15,6 +15,7 @@ import type { DamageKind as DamageKindType, DamageOrigin as DamageOriginType, Sc import { damageTakenFactor, effectiveAttrs, healDealtFactor, healTakenFactor, isStanding, recoverableHp } from "./units.ts" export type StrikeInput = { + percentageDamage?: boolean source: Unit target: Unit kind: DamageKindType @@ -88,6 +89,7 @@ export function resolveStrike(ctx: BattleContext, input: StrikeInput): void { raw = floorAtLeast(MIN_DAMAGE, raw * damageTakenFactor(target, input.kind)) const hooked = ctx.hooks.emit(HookName.OnHitCalc, { + percentageDamage: input.percentageDamage, source, target, damage: raw, @@ -367,6 +369,7 @@ export function applyHeal(ctx: BattleContext, source: Unit, target: Unit, power: ctx.currentAction?.primaryTargetId !== undefined && target.id === ctx.currentAction.primaryTargetId const taken = healTakenFactor(target) * (fixedBase ? 1 : healDealtFactor(source)) const hooked = ctx.hooks.emit(HookName.OnHealCalc, { + origin: ctx.suppressHooks > 0 ? DamageOrigin.HookDerived : DamageOrigin.ActionDirect, source, target, heal: amount * taken, diff --git a/src/shared/engine/combat-v6/core/effects.ts b/src/shared/engine/combat-v6/core/effects.ts index 2e15122e..b61e017f 100644 --- a/src/shared/engine/combat-v6/core/effects.ts +++ b/src/shared/engine/combat-v6/core/effects.ts @@ -5,7 +5,7 @@ import { DEFAULT_HITS } from "./constants.ts" import type { BattleContext } from "./context.ts" import { applyBarrier } from "./barriers.ts" import { applyHeal, applyHpRestore, applyMpDamage, applyRevive, applyWound, changeWound, resolveStrike } from "./damage.ts" -import { DamageKind, EffectType, EventType, FailReason, FormulaFamily, StatusCategory, StatusHit, StatusRemoveReason } from "./enums.ts" +import { DamageKind, DamageOrigin, EffectType, EventType, FailReason, FormulaFamily, StatusCategory, StatusHit, StatusRemoveReason } from "./enums.ts" import { evalExpr } from "./expr.ts" import { atLeast, floorAtLeast } from "./math.ts" import { applyStatus, copyStatusInstance, envFor, removeStatus } from "./status.ts" @@ -63,6 +63,7 @@ const handlers: { [K in SkillEffect["type"]]?: EffectHandler 0 ? DamageOrigin.HookDerived : DamageOrigin.ActionDirect, source, target, barrier: evalExpr(effect.power, { ...env, target }), @@ -397,6 +398,7 @@ function handleHit( // 横扫中途打死目标则后续刀取消。 if (!isStanding(source) || !isStanding(t) || ctx.state.result) break resolveStrike(ctx, { + percentageDamage: effect.type === EffectType.FixedHit ? effect.percentageDamage : undefined, source, target: t, kind, diff --git a/src/shared/engine/combat-v6/core/hooks.ts b/src/shared/engine/combat-v6/core/hooks.ts index 8cb77055..d642ae34 100644 --- a/src/shared/engine/combat-v6/core/hooks.ts +++ b/src/shared/engine/combat-v6/core/hooks.ts @@ -30,6 +30,7 @@ export type HookContext = { isPrimary?: boolean crit?: boolean chance?: number + percentageDamage?: boolean } export type HookFn = (ctx: HookContext) => void diff --git a/src/shared/engine/combat-v6/core/passives.ts b/src/shared/engine/combat-v6/core/passives.ts index a9560bcd..8f05bc5f 100644 --- a/src/shared/engine/combat-v6/core/passives.ts +++ b/src/shared/engine/combat-v6/core/passives.ts @@ -61,6 +61,7 @@ export function bindDataHooks(ctx: BattleContext): void { kind: hctx.kind ?? hook.requireKind, origin: hctx.origin, isPrimary: hctx.isPrimary, + percentageDamage: hctx.percentageDamage, markKey: `${skill.id}:${hookIndex}`, } if (!matchesWhen(ctx, hook.when, scope)) return diff --git a/src/shared/engine/combat-v6/core/types.ts b/src/shared/engine/combat-v6/core/types.ts index 00830b17..f3db87cf 100644 --- a/src/shared/engine/combat-v6/core/types.ts +++ b/src/shared/engine/combat-v6/core/types.ts @@ -314,6 +314,14 @@ export type SkillTargeting = { /** 钩子/效果的通用过滤。引擎只做匹配,不要在这里写门派名。 */ export type EffectWhen = { + excludeSkillTags?: SkillTag[]; + excludePercentageDamage?: boolean; + sourceMpRatioBelow?: number; + sourceMpRatioAbove?: number; + sourceHasBarrier?: boolean; + targetHasBarrier?: boolean; + sourceStatusCategories?: StatusCategory[]; + sourceRemovableControl?: boolean; skillIds?: SkillId[]; skillTags?: SkillTag[]; requireStatusIds?: StatusId[]; @@ -391,6 +399,8 @@ type EffectCore = } | { type: typeof EffectType.FixedHit; + /** Damage based on the target's current/maximum HP, not an ordinary attack. */ + percentageDamage?: boolean; hits?: Expr; coeff?: number | number[]; /** Multiplies the resolved damage, rather than the attack formula. */ diff --git a/src/shared/engine/combat-v6/core/when.ts b/src/shared/engine/combat-v6/core/when.ts index d0991614..03857c36 100644 --- a/src/shared/engine/combat-v6/core/when.ts +++ b/src/shared/engine/combat-v6/core/when.ts @@ -15,6 +15,7 @@ export type WhenScope = { kind?: DamageKind origin?: DamageOrigin isPrimary?: boolean + percentageDamage?: boolean /** 写入 marks 的前缀,通常是被动技能 id + 钩子下标 */ markKey?: string } @@ -64,6 +65,18 @@ export function matchesWhen(ctx: Pick skill?.tags.includes(tag))) return false + if (when.excludePercentageDamage && scope.percentageDamage) return false + const mpRatio = scope.source.attrs.mp / Math.max(1, scope.source.attrs.maxMp) + if (when.sourceMpRatioBelow !== undefined && mpRatio >= when.sourceMpRatioBelow) return false + if (when.sourceMpRatioAbove !== undefined && mpRatio <= when.sourceMpRatioAbove) return false + if (when.sourceHasBarrier !== undefined && scope.source.barriers.some(b => b.current > 0) !== when.sourceHasBarrier) return false + if (when.targetHasBarrier !== undefined && (!scope.target || scope.target.barriers.some(b => b.current > 0) !== when.targetHasBarrier)) return false + if (when.sourceStatusCategories && !hasCategory(ctx, scope.source, when.sourceStatusCategories)) return false + if (when.sourceRemovableControl && !scope.source.statuses.some(s => { + const def = ctx.statusDefs.get(s.id) + return def?.category === 'control' && def.dispellable !== false && !def.blocksRevive + })) return false const isPrimary = scope.isPrimary ?? (scope.target !== undefined && scope.target.id === ctx.currentAction?.primaryTargetId) diff --git a/src/shared/engine/combat-v6/manuals/attributes.ts b/src/shared/engine/combat-v6/manuals/attributes.ts new file mode 100644 index 00000000..52a50e59 --- /dev/null +++ b/src/shared/engine/combat-v6/manuals/attributes.ts @@ -0,0 +1,9 @@ +import type { CharacterManualDefV1 } from './types'; + +/** valueAt1 is the complete first-level bonus; growth starts at level two. */ +export function manualAttributeValue( + effect: CharacterManualDefV1['effects'][number], + level: number, +): number { + return effect.valueAt1 + effect.valuePerLevel * (level - 1); +} diff --git a/src/shared/engine/combat-v6/manuals/compiler.ts b/src/shared/engine/combat-v6/manuals/compiler.ts index c95e9507..849b2b47 100644 --- a/src/shared/engine/combat-v6/manuals/compiler.ts +++ b/src/shared/engine/combat-v6/manuals/compiler.ts @@ -3,7 +3,10 @@ import type { CombatV6ProjectionDiagnostic, CultivatorBaseCombatInput, } from '../projection/types.ts'; +import { DaoyouRule } from '../rules-daoyou/constants'; +import { manualAttributeValue } from './attributes'; import { CHARACTER_MANUALS_V1, manualRule } from './content.ts'; +import { compileManualSkill, manualMechanismValue } from './mechanism'; import { MANUAL_REALMS } from './pack.ts'; import type { CharacterManualDefV1, @@ -13,7 +16,7 @@ import type { ManualSlotV1, } from './types.ts'; -export const MAX_MANUALS_PER_SLOT = 3; +export const MAX_MANUALS_PER_SLOT = 6; export function getManualSlotCount(realm: RealmType): number { return Math.min(4, REALM_VALUES.indexOf(realm) + 1); @@ -60,7 +63,7 @@ export function validateManualStateV1( const slot = manualSlot(def); const count = (learnedCounts.get(slot) ?? 0) + 1; learnedCounts.set(slot, count); - if (count > MAX_MANUALS_PER_SLOT) fail('每个境界位最多学习三种功法'); + if (count > MAX_MANUALS_PER_SLOT) fail('每个境界位最多学习六种功法'); const rule = manualRule(def); if (manualSlot(def) > getManualSlotCount(realm)) fail('当前境界不能修炼该功法'); @@ -120,8 +123,16 @@ export function compileCharacterManualsV1( for (const effect of def.effects) projection.attributeBonuses[effect.attribute] = (projection.attributeBonuses[effect.attribute] ?? 0) + - effect.valuePerLevel * level; - projection.skills.push(def.skill); + manualAttributeValue(effect, level); + projection.skills.push(compileManualSkill(def, level)); + if (def.mechanism.type === 'sealResist') + projection.panel.push({ + attr: 'sealResist', + mode: 'add', + value: + manualMechanismValue(def.mechanism, level) * + DaoyouRule.hitChanceScale, + }); projection.passiveSkillIds.push(def.skill.id); projection.skillLevels[def.skill.id] = level; projection.capabilities.push({ diff --git a/src/shared/engine/combat-v6/manuals/data/manual-pack.json b/src/shared/engine/combat-v6/manuals/data/manual-pack.json index c02041e4..dff95e8c 100644 --- a/src/shared/engine/combat-v6/manuals/data/manual-pack.json +++ b/src/shared/engine/combat-v6/manuals/data/manual-pack.json @@ -1,6 +1,6 @@ { "$schema": "./manual-pack.schema.json", - "version": 1, + "version": 3, "progressions": { "standard": { "maxLevel": 9, @@ -10,139 +10,139 @@ ], "costsByRealm": { "炼气": [ - { - "experience": 20, - "insight": 2 - }, { "experience": 40, - "insight": 3 - }, - { - "experience": 60, "insight": 4 }, { "experience": 80, - "insight": 5 - }, - { - "experience": 100, "insight": 6 }, { "experience": 120, - "insight": 7 - }, - { - "experience": 140, "insight": 8 }, { "experience": 160, - "insight": 9 + "insight": 10 + }, + { + "experience": 200, + "insight": 12 + }, + { + "experience": 240, + "insight": 14 + }, + { + "experience": 280, + "insight": 16 + }, + { + "experience": 320, + "insight": 18 } ], "筑基": [ - { - "experience": 100, - "insight": 2 - }, { "experience": 200, - "insight": 3 - }, - { - "experience": 300, - "insight": 4 - }, - { - "experience": 400, - "insight": 5 - }, - { - "experience": 500, "insight": 6 }, { - "experience": 600, - "insight": 7 + "experience": 400, + "insight": 9 }, { - "experience": 700, - "insight": 8 + "experience": 600, + "insight": 12 }, { "experience": 800, - "insight": 9 + "insight": 15 + }, + { + "experience": 1000, + "insight": 18 + }, + { + "experience": 1200, + "insight": 21 + }, + { + "experience": 1400, + "insight": 24 + }, + { + "experience": 1600, + "insight": 27 } ], "金丹": [ - { - "experience": 500, - "insight": 2 - }, { "experience": 1000, - "insight": 3 - }, - { - "experience": 1500, - "insight": 4 - }, - { - "experience": 2000, - "insight": 5 - }, - { - "experience": 2500, - "insight": 6 - }, - { - "experience": 3000, - "insight": 7 - }, - { - "experience": 3500, "insight": 8 }, + { + "experience": 2000, + "insight": 12 + }, + { + "experience": 3000, + "insight": 16 + }, { "experience": 4000, - "insight": 9 + "insight": 20 + }, + { + "experience": 5000, + "insight": 24 + }, + { + "experience": 6000, + "insight": 28 + }, + { + "experience": 7000, + "insight": 32 + }, + { + "experience": 8000, + "insight": 36 } ], "元婴": [ - { - "experience": 2000, - "insight": 2 - }, { "experience": 4000, - "insight": 3 - }, - { - "experience": 6000, - "insight": 4 + "insight": 10 }, { "experience": 8000, - "insight": 5 - }, - { - "experience": 10000, - "insight": 6 + "insight": 15 }, { "experience": 12000, - "insight": 7 - }, - { - "experience": 14000, - "insight": 8 + "insight": 20 }, { "experience": 16000, - "insight": 9 + "insight": 25 + }, + { + "experience": 20000, + "insight": 30 + }, + { + "experience": 24000, + "insight": 35 + }, + { + "experience": 28000, + "insight": 40 + }, + { + "experience": 32000, + "insight": 45 } ] } @@ -153,257 +153,537 @@ "id": "character_manual.changchun", "name": "长春功", "realm": "炼气", - "rarity": "common", - "description": "温养肉身,培固根本。", + "description": "生机绵长,温养自身。", "progressionId": "standard", - "dropWeight": 100, "effects": [ { "attribute": "vitality", - "valuePerLevel": 1 + "valuePerLevel": 2, + "valueAt1": 12 } - ] + ], + "mechanism": { + "type": "restoreHp", + "condition": "selfHpBelow50", + "valueAt1": 0.0025, + "valueAt9": 0.0075 + } }, { - "id": "character_manual.qingfeng", - "name": "轻风诀", + "id": "character_manual.gengjin", + "name": "庚金诀", "realm": "炼气", - "rarity": "common", - "description": "行气轻灵,身随风动。", + "description": "金气锐利,先破敌锋。", "progressionId": "standard", - "dropWeight": 100, - "effects": [ - { - "attribute": "speed", - "valuePerLevel": 1 - } - ] - }, - { - "id": "character_manual.yangshen", - "name": "养神诀", - "realm": "炼气", - "rarity": "common", - "description": "澄明灵台,凝聚神元。", - "progressionId": "standard", - "dropWeight": 100, - "effects": [ - { - "attribute": "spirit", - "valuePerLevel": 1 - } - ] - }, - { - "id": "character_manual.songhe", - "name": "松鹤养元经", - "realm": "炼气", - "rarity": "rare", - "description": "温养肉身,培固根本。", - "progressionId": "standard", - "dropWeight": 5, - "effects": [ - { - "attribute": "vitality", - "valuePerLevel": 1 - }, - { - "attribute": "willpower", - "valuePerLevel": 1 - } - ] - }, - { - "id": "character_manual.houtu", - "name": "厚土功", - "realm": "筑基", - "rarity": "common", - "description": "厚积根基,坚韧如山。", - "progressionId": "standard", - "dropWeight": 100, - "effects": [ - { - "attribute": "endurance", - "valuePerLevel": 1 - } - ] - }, - { - "id": "character_manual.ningyuan", - "name": "凝元诀", - "realm": "筑基", - "rarity": "common", - "description": "澄明灵台,凝聚神元。", - "progressionId": "standard", - "dropWeight": 100, - "effects": [ - { - "attribute": "spirit", - "valuePerLevel": 1 - } - ] - }, - { - "id": "character_manual.duanyue", - "name": "断岳功", - "realm": "筑基", - "rarity": "common", - "description": "凝炼劲力,发力沉雄。", - "progressionId": "standard", - "dropWeight": 100, "effects": [ { "attribute": "strength", - "valuePerLevel": 1 + "valuePerLevel": 2, + "valueAt1": 12 } - ] + ], + "mechanism": { + "type": "damage", + "condition": "targetHpAbove70", + "valueAt1": 0.01, + "valueAt9": 0.03, + "kinds": [ + "physical" + ] + } }, { - "id": "character_manual.jinque", - "name": "金阙固元经", + "id": "character_manual.qingmu", + "name": "青木养元诀", + "realm": "炼气", + "description": "木气滋生,扶伤续命。", + "progressionId": "standard", + "effects": [ + { + "attribute": "willpower", + "valuePerLevel": 2, + "valueAt1": 12 + } + ], + "mechanism": { + "type": "heal", + "condition": "targetHpBelow50", + "valueAt1": 0.03, + "valueAt9": 0.09 + } + }, + { + "id": "character_manual.qingquan", + "name": "清泉诀", + "realm": "炼气", + "description": "涓流回补,法力不竭。", + "progressionId": "standard", + "effects": [ + { + "attribute": "spirit", + "valuePerLevel": 2, + "valueAt1": 12 + } + ], + "mechanism": { + "type": "restoreMp", + "condition": "selfMpBelow50", + "valueAt1": 0.0025, + "valueAt9": 0.0075 + } + }, + { + "id": "character_manual.chiyan", + "name": "赤炎诀", + "realm": "炼气", + "description": "精气充盈,火势方盛。", + "progressionId": "standard", + "effects": [ + { + "attribute": "spirit", + "valuePerLevel": 2, + "valueAt1": 12 + } + ], + "mechanism": { + "type": "damage", + "condition": "selfHpAbove70", + "valueAt1": 0.01, + "valueAt9": 0.03, + "kinds": [ + "spell" + ] + } + }, + { + "id": "character_manual.houtu_jue", + "name": "厚土诀", + "realm": "炼气", + "description": "厚土承压,守势稳固。", + "progressionId": "standard", + "effects": [ + { + "attribute": "endurance", + "valuePerLevel": 2, + "valueAt1": 12 + } + ], + "mechanism": { + "type": "mitigation", + "condition": "defending", + "valueAt1": 0.03, + "valueAt9": 0.09, + "kinds": [ + "physical" + ] + } + }, + { + "id": "character_manual.guiyuan", + "name": "归元功", "realm": "筑基", - "rarity": "rare", - "description": "厚积根基,坚韧如山。", + "description": "真元归一,凝甲护身。", "progressionId": "standard", - "dropWeight": 5, "effects": [ { "attribute": "endurance", - "valuePerLevel": 1 - }, - { - "attribute": "vitality", - "valuePerLevel": 1 + "valuePerLevel": 2, + "valueAt1": 18 } - ] + ], + "mechanism": { + "type": "mitigation", + "condition": "selfBarrier", + "valueAt1": 0.02, + "valueAt9": 0.06, + "kinds": [ + "physical", + "spell" + ] + } }, { - "id": "character_manual.jianyu", - "name": "剑元诀", - "realm": "金丹", - "rarity": "common", - "description": "凝炼劲力,发力沉雄。", + "id": "character_manual.huanling", + "name": "幻灵诀", + "realm": "筑基", + "description": "幻影扰目,难辨真身。", "progressionId": "standard", - "dropWeight": 100, - "effects": [ - { - "attribute": "strength", - "valuePerLevel": 2 - } - ] - }, - { - "id": "character_manual.dinghun", - "name": "定魂功", - "realm": "金丹", - "rarity": "common", - "description": "凝神守一,心志不移。", - "progressionId": "standard", - "dropWeight": 100, - "effects": [ - { - "attribute": "willpower", - "valuePerLevel": 2 - } - ] - }, - { - "id": "character_manual.liuyun", - "name": "流云诀", - "realm": "金丹", - "rarity": "common", - "description": "行气轻灵,身随风动。", - "progressionId": "standard", - "dropWeight": 100, "effects": [ { "attribute": "speed", - "valuePerLevel": 2 + "valuePerLevel": 2, + "valueAt1": 18 } - ] + ], + "mechanism": { + "type": "evasion", + "condition": "always", + "valueAt1": 0.01, + "valueAt9": 0.03 + } }, { - "id": "character_manual.zixiao", - "name": "紫霄养神经", - "realm": "金丹", - "rarity": "rare", - "description": "澄明灵台,凝聚神元。", + "id": "character_manual.qingyuan", + "name": "青元剑诀", + "realm": "筑基", + "description": "真元养剑,气盛剑锐。", "progressionId": "standard", - "dropWeight": 5, "effects": [ { - "attribute": "spirit", - "valuePerLevel": 2 - }, + "attribute": "strength", + "valuePerLevel": 2, + "valueAt1": 18 + } + ], + "mechanism": { + "type": "damage", + "condition": "selfHpAbove70", + "valueAt1": 0.02, + "valueAt9": 0.04, + "kinds": [ + "physical" + ] + } + }, + { + "id": "character_manual.dayan", + "name": "大衍诀", + "realm": "筑基", + "description": "壮大神识,分神守心。", + "progressionId": "standard", + "effects": [ { "attribute": "willpower", - "valuePerLevel": 2 + "valuePerLevel": 2, + "valueAt1": 18 } - ] + ], + "mechanism": { + "type": "sealResist", + "condition": "always", + "valueAt1": 0.01, + "valueAt9": 0.03 + } }, { - "id": "character_manual.taiqing", - "name": "太清诀", - "realm": "元婴", - "rarity": "common", - "description": "澄明灵台,凝聚神元。", + "id": "character_manual.sanzhuan", + "name": "三转重元功", + "realm": "筑基", + "description": "真元重炼,压缩精纯。", "progressionId": "standard", - "dropWeight": 100, "effects": [ { "attribute": "spirit", - "valuePerLevel": 2 + "valuePerLevel": 2, + "valueAt1": 18 } - ] + ], + "mechanism": { + "type": "damage", + "condition": "selfMpAbove70", + "valueAt1": 0.02, + "valueAt9": 0.04, + "kinds": [ + "spell" + ] + } }, { - "id": "character_manual.changsheng", - "name": "长生功", - "realm": "元婴", - "rarity": "common", - "description": "温养肉身,培固根本。", + "id": "character_manual.shayao", + "name": "煞妖诀", + "realm": "筑基", + "description": "血煞催身,险中争胜。", "progressionId": "standard", - "dropWeight": 100, "effects": [ { "attribute": "vitality", - "valuePerLevel": 2 + "valuePerLevel": 2, + "valueAt1": 18 } - ] + ], + "mechanism": { + "type": "damage", + "condition": "selfHpBelow35", + "valueAt1": 0.02, + "valueAt9": 0.04, + "kinds": [ + "physical", + "spell", + "fixed" + ] + } }, { - "id": "character_manual.zhenyue", - "name": "镇岳功", - "realm": "元婴", - "rarity": "common", - "description": "厚积根基,坚韧如山。", + "id": "character_manual.taibai", + "name": "太白剑诀", + "realm": "金丹", + "description": "剑气破障,锋芒穿护。", + "progressionId": "standard", + "effects": [ + { + "attribute": "strength", + "valuePerLevel": 4, + "valueAt1": 24 + } + ], + "mechanism": { + "type": "damage", + "condition": "targetBarrier", + "valueAt1": 0.03, + "valueAt9": 0.06, + "kinds": [ + "physical" + ] + } + }, + { + "id": "character_manual.taiyang", + "name": "泰阳诀", + "realm": "金丹", + "description": "精纯火力,盛阳外放。", + "progressionId": "standard", + "effects": [ + { + "attribute": "spirit", + "valuePerLevel": 4, + "valueAt1": 24 + } + ], + "mechanism": { + "type": "damage", + "condition": "selfHpAbove70", + "valueAt1": 0.03, + "valueAt9": 0.06, + "kinds": [ + "spell" + ] + } + }, + { + "id": "character_manual.xuanyin", + "name": "玄阴大法", + "realm": "金丹", + "description": "阴魂蚀体,阴气乘隙。", + "progressionId": "standard", + "effects": [ + { + "attribute": "spirit", + "valuePerLevel": 4, + "valueAt1": 24 + } + ], + "mechanism": { + "type": "damage", + "condition": "targetDot", + "valueAt1": 0.03, + "valueAt9": 0.06, + "kinds": [ + "spell", + "fixed" + ] + } + }, + { + "id": "character_manual.yudan", + "name": "玉丹功", + "realm": "金丹", + "description": "玉润丹养,扶危固本。", + "progressionId": "standard", + "effects": [ + { + "attribute": "willpower", + "valuePerLevel": 4, + "valueAt1": 24 + } + ], + "mechanism": { + "type": "heal", + "condition": "targetHpBelow50", + "valueAt1": 0.04, + "valueAt9": 0.12 + } + }, + { + "id": "character_manual.tianluo", + "name": "天罗真功", + "realm": "金丹", + "description": "儒门正气,护佑危局。", + "progressionId": "standard", + "effects": [ + { + "attribute": "willpower", + "valuePerLevel": 4, + "valueAt1": 24 + } + ], + "mechanism": { + "type": "barrier", + "condition": "targetHpBelow50", + "valueAt1": 0.05, + "valueAt9": 0.15 + } + }, + { + "id": "character_manual.liuji", + "name": "六极真魔功", + "realm": "金丹", + "description": "真魔法体,受压显威。", + "progressionId": "standard", + "effects": [ + { + "attribute": "strength", + "valuePerLevel": 4, + "valueAt1": 24 + } + ], + "mechanism": { + "type": "damage", + "condition": "selfHpBelow35", + "valueAt1": 0.03, + "valueAt9": 0.06, + "kinds": [ + "physical", + "spell" + ] + } + }, + { + "id": "character_manual.tuotian", + "name": "托天魔功", + "realm": "元婴", + "description": "肉身强横,重压不折。", "progressionId": "standard", - "dropWeight": 100, "effects": [ { "attribute": "endurance", - "valuePerLevel": 2 + "valuePerLevel": 4, + "valueAt1": 36 } - ] + ], + "mechanism": { + "type": "mitigation", + "condition": "selfHpBelow50", + "valueAt1": 0.03, + "valueAt9": 0.07, + "kinds": [ + "physical" + ] + } }, { - "id": "character_manual.shenxiao", - "name": "神霄御风经", + "id": "character_manual.mingwang", + "name": "明王诀", "realm": "元婴", - "rarity": "rare", - "description": "凝炼劲力,发力沉雄。", + "description": "法体坚固,受制不溃。", + "progressionId": "standard", + "effects": [ + { + "attribute": "vitality", + "valuePerLevel": 4, + "valueAt1": 36 + } + ], + "mechanism": { + "type": "mitigation", + "condition": "selfControl", + "valueAt1": 0.04, + "valueAt9": 0.1, + "kinds": [ + "physical", + "spell" + ] + } + }, + { + "id": "character_manual.yuanci", + "name": "元磁神光", + "realm": "元婴", + "description": "元磁牵制,护持周身。", + "progressionId": "standard", + "effects": [ + { + "attribute": "spirit", + "valuePerLevel": 4, + "valueAt1": 36 + } + ], + "mechanism": { + "type": "mitigation", + "condition": "selfMpAbove50", + "valueAt1": 0.03, + "valueAt9": 0.07, + "kinds": [ + "spell" + ] + } + }, + { + "id": "character_manual.haoran", + "name": "浩然正气诀", + "realm": "元婴", + "description": "邪扰不侵,正气护神。", + "progressionId": "standard", + "effects": [ + { + "attribute": "willpower", + "valuePerLevel": 4, + "valueAt1": 36 + } + ], + "mechanism": { + "type": "mitigation", + "condition": "selfDebuff", + "valueAt1": 0.04, + "valueAt9": 0.1, + "kinds": [ + "spell" + ] + } + }, + { + "id": "character_manual.qianlang", + "name": "千浪诀", + "realm": "元婴", + "description": "身随浪走,危中脱身。", + "progressionId": "standard", + "effects": [ + { + "attribute": "speed", + "valuePerLevel": 4, + "valueAt1": 36 + } + ], + "mechanism": { + "type": "evasion", + "condition": "selfHpBelow50", + "valueAt1": 0.02, + "valueAt9": 0.06 + } + }, + { + "id": "character_manual.yousha", + "name": "幽杀诀", + "realm": "元婴", + "description": "杀意逼迫,追索残敌。", "progressionId": "standard", - "dropWeight": 5, "effects": [ { "attribute": "strength", - "valuePerLevel": 2 - }, - { - "attribute": "speed", - "valuePerLevel": 2 + "valuePerLevel": 4, + "valueAt1": 36 } - ] + ], + "mechanism": { + "type": "damage", + "condition": "targetHpBelow35", + "valueAt1": 0.03, + "valueAt9": 0.06, + "kinds": [ + "physical", + "spell", + "fixed" + ] + } } ] } diff --git a/src/shared/engine/combat-v6/manuals/data/manual-pack.schema.json b/src/shared/engine/combat-v6/manuals/data/manual-pack.schema.json index 1a5d0d4f..ca417263 100644 --- a/src/shared/engine/combat-v6/manuals/data/manual-pack.schema.json +++ b/src/shared/engine/combat-v6/manuals/data/manual-pack.schema.json @@ -7,7 +7,7 @@ }, "version": { "type": "number", - "const": 1 + "const": 3 }, "progressions": { "type": "object", @@ -85,88 +85,7 @@ "minItems": 1, "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "pattern": "^character_manual\\.[a-z][a-z0-9_-]*$" - }, - "name": { - "type": "string", - "minLength": 1 - }, - "realm": { - "type": "string", - "enum": [ - "炼气", - "筑基", - "金丹", - "元婴" - ] - }, - "rarity": { - "type": "string", - "enum": [ - "common", - "rare" - ] - }, - "description": { - "type": "string", - "minLength": 1 - }, - "progressionId": { - "type": "string", - "minLength": 1 - }, - "dropWeight": { - "type": "integer", - "exclusiveMinimum": 0, - "maximum": 1000000000 - }, - "effects": { - "minItems": 1, - "maxItems": 2, - "type": "array", - "items": { - "type": "object", - "properties": { - "attribute": { - "type": "string", - "enum": [ - "vitality", - "strength", - "spirit", - "endurance", - "speed", - "willpower" - ] - }, - "valuePerLevel": { - "type": "integer", - "minimum": 1, - "maximum": 100 - } - }, - "required": [ - "attribute", - "valuePerLevel" - ], - "additionalProperties": false - } - } - }, - "required": [ - "id", - "name", - "realm", - "rarity", - "description", - "progressionId", - "dropWeight", - "effects" - ], - "additionalProperties": false + "$ref": "#/$defs/__schema0" } } }, @@ -175,5 +94,237 @@ "progressions", "manuals" ], - "additionalProperties": false + "additionalProperties": false, + "$defs": { + "__schema0": { + "type": "object", + "properties": { + "id": { + "type": "string", + "pattern": "^character_manual\\.[a-z][a-z0-9_-]*$" + }, + "name": { + "type": "string", + "minLength": 1 + }, + "realm": { + "type": "string", + "enum": [ + "炼气", + "筑基", + "金丹", + "元婴" + ] + }, + "description": { + "type": "string", + "minLength": 1 + }, + "progressionId": { + "type": "string", + "minLength": 1 + }, + "effects": { + "minItems": 1, + "maxItems": 1, + "type": "array", + "items": { + "$ref": "#/$defs/__schema1" + } + }, + "mechanism": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "damage" + }, + "condition": { + "$ref": "#/$defs/__schema2" + }, + "kinds": { + "minItems": 1, + "maxItems": 3, + "type": "array", + "items": { + "$ref": "#/$defs/__schema3" + } + }, + "valueAt1": { + "$ref": "#/$defs/__schema4" + }, + "valueAt9": { + "$ref": "#/$defs/__schema5" + } + }, + "required": [ + "type", + "condition", + "kinds", + "valueAt1", + "valueAt9" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "mitigation" + }, + "condition": { + "$ref": "#/$defs/__schema2" + }, + "kinds": { + "minItems": 1, + "maxItems": 2, + "type": "array", + "items": { + "$ref": "#/$defs/__schema6" + } + }, + "valueAt1": { + "$ref": "#/$defs/__schema4" + }, + "valueAt9": { + "$ref": "#/$defs/__schema5" + } + }, + "required": [ + "type", + "condition", + "kinds", + "valueAt1", + "valueAt9" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "heal", + "barrier", + "evasion", + "sealResist", + "restoreHp", + "restoreMp" + ] + }, + "condition": { + "$ref": "#/$defs/__schema2" + }, + "valueAt1": { + "$ref": "#/$defs/__schema4" + }, + "valueAt9": { + "$ref": "#/$defs/__schema5" + } + }, + "required": [ + "type", + "condition", + "valueAt1", + "valueAt9" + ], + "additionalProperties": false + } + ] + } + }, + "required": [ + "id", + "name", + "realm", + "description", + "progressionId", + "effects", + "mechanism" + ], + "additionalProperties": false + }, + "__schema1": { + "type": "object", + "properties": { + "attribute": { + "type": "string", + "enum": [ + "vitality", + "strength", + "spirit", + "endurance", + "speed", + "willpower" + ] + }, + "valueAt1": { + "type": "integer", + "minimum": 1, + "maximum": 1000 + }, + "valuePerLevel": { + "type": "integer", + "minimum": 1, + "maximum": 100 + } + }, + "required": [ + "attribute", + "valueAt1", + "valuePerLevel" + ], + "additionalProperties": false + }, + "__schema2": { + "type": "string", + "enum": [ + "always", + "selfHpBelow50", + "selfHpBelow35", + "selfHpAbove70", + "targetHpBelow50", + "targetHpBelow35", + "targetHpAbove70", + "selfMpBelow50", + "selfMpAbove50", + "selfMpAbove70", + "defending", + "selfBarrier", + "targetBarrier", + "targetDot", + "selfControl", + "selfDebuff" + ] + }, + "__schema3": { + "type": "string", + "enum": [ + "physical", + "spell", + "fixed" + ] + }, + "__schema4": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 0.2 + }, + "__schema5": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 0.2 + }, + "__schema6": { + "type": "string", + "enum": [ + "physical", + "spell" + ] + } + } } diff --git a/src/shared/engine/combat-v6/manuals/learning-limit.test.ts b/src/shared/engine/combat-v6/manuals/learning-limit.test.ts index b32d2de3..c15e2360 100644 --- a/src/shared/engine/combat-v6/manuals/learning-limit.test.ts +++ b/src/shared/engine/combat-v6/manuals/learning-limit.test.ts @@ -25,7 +25,7 @@ function fullSlot(): CultivatorManualStateV1 { learned: [], build: { slots: [] }, }; - for (const manual of manuals.slice(0, 3)) { + for (const manual of manuals) { const result = learn(state, manual); if (!result.ok) throw new Error(result.diagnostics[0].message); state = result.state; @@ -33,11 +33,11 @@ function fullSlot(): CultivatorManualStateV1 { return state; } -describe('功法每境界三种学习上限', () => { - it('第三种可以学习,第四种拒绝且保留全部进度和激活项', () => { +describe('功法每境界六种学习上限', () => { + it('六种可以全部学习,重复学习拒绝且保留进度和激活项', () => { const state = fullSlot(); const before = structuredClone(state); - expect(state.learned).toHaveLength(3); + expect(state.learned).toHaveLength(6); expect(state.build.slots).toEqual([{ slot: 1, manualId: manuals[0].id }]); expect(learn(state, manuals[3])).toMatchObject({ ok: false }); expect(state).toEqual(before); @@ -48,7 +48,7 @@ describe('功法每境界三种学习上限', () => { state, CHARACTER_MANUALS_V1.find((m) => m.realm === '筑基')!, ); - expect(result.ok && result.state.learned).toHaveLength(4); + expect(result.ok && result.state.learned).toHaveLength(7); expect(result.ok && result.state.build.slots).toHaveLength(2); }); it('学满后仍可修炼、用同名玉简突破及免费切换', () => { @@ -66,16 +66,17 @@ describe('功法每境界三种学习上限', () => { expect(result.ok).toBe(true); if (result.ok) state = result.state; } - expect(state.learned).toHaveLength(3); + expect(state.learned).toHaveLength(6); expect(state.learned[0]).toMatchObject({ level: 3, unlockedLevel: 6 }); expect(state.build.slots[0].manualId).toBe(manuals[1].id); }); - it('状态校验拒绝同境界超过三种功法', () => { + it('状态校验拒绝同境界超过六种功法', () => { const state = fullSlot(); - state.learned.push({ manualId: manuals[3].id, level: 1, unlockedLevel: 3 }); - expect(validateManualStateV1(state, '筑基')).toEqual( + const extra = { ...manuals[0], id: 'character_manual.extra' }; + state.learned.push({ manualId: extra.id, level: 1, unlockedLevel: 3 }); + expect(validateManualStateV1(state, '筑基', [...CHARACTER_MANUALS_V1, extra])).toEqual( expect.arrayContaining([ - expect.objectContaining({ message: '每个境界位最多学习三种功法' }), + expect.objectContaining({ message: '每个境界位最多学习六种功法' }), ]), ); }); diff --git a/src/shared/engine/combat-v6/manuals/manuals.test.ts b/src/shared/engine/combat-v6/manuals/manuals.test.ts index 0b8d63ad..115965fe 100644 --- a/src/shared/engine/combat-v6/manuals/manuals.test.ts +++ b/src/shared/engine/combat-v6/manuals/manuals.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; +import { manualJadeCost } from '../../../manuals/action'; import { compileCharacterPanelV1 } from '../projection/character-panel-v1'; import { resolveCombatCapabilitiesV1 } from './capabilities'; import { @@ -50,16 +51,16 @@ describe('境界功法培养', () => { changeManual({ state: empty(), action: 'learn', - manualId: 'character_manual.houtu', + manualId: 'character_manual.guiyuan', slot: 2, realm: '炼气', expectedRevision: 0, resources: { experience: 0, insight: 0 }, }).ok, ).toBe(false); - expect(act(empty(), 'learn', 'character_manual.houtu').ok).toBe(false); + expect(act(empty(), 'learn', 'character_manual.guiyuan').ok).toBe(false); }); - it('三本玉简与八次修炼完成九层,瓶颈只解锁不跳层', () => { + it('六本玉简与八次修炼完成九层,瓶颈只解锁不跳层', () => { const original = empty(); let state = success(act(original, 'learn')); expect(original).toEqual(empty()); @@ -69,27 +70,27 @@ describe('境界功法培养', () => { if (p.level === p.unlockedLevel) { expect(act(state, 'train').ok).toBe(false); const before = p.level; + jades += manualJadeCost(state, { action: 'unlock', manualId: id }); state = success(act(state, 'unlock')); expect(state.learned[0].level).toBe(before); expect(act(state, 'unlock').ok).toBe(false); - jades++; } const result = act(state, 'train'); expect(result.ok && result.cost.experience).toBeGreaterThan(0); state = success(result); expect(state.learned[0].level).toBe(target); } - expect(jades).toBe(3); + expect(jades).toBe(6); expect(act(state, 'train').ok).toBe(false); expect(act(state, 'unlock').ok).toBe(false); }); it('多个功法独立培养,免费激活保留原有进度', () => { let state = success(act(empty(), 'learn')); state = success(act(state, 'train')); - state = success(act(state, 'learn', 'character_manual.qingfeng')); - state = success(act(state, 'train', 'character_manual.qingfeng')); + state = success(act(state, 'learn', 'character_manual.gengjin')); + state = success(act(state, 'train', 'character_manual.gengjin')); expect(state.build.slots[0].manualId).toBe(id); - const switched = act(state, 'activate', 'character_manual.qingfeng'); + const switched = act(state, 'activate', 'character_manual.gengjin'); expect(switched.ok && switched.cost).toEqual({ experience: 0, insight: 0 }); state = success(switched); state = success(act(state, 'activate')); @@ -108,21 +109,21 @@ describe('境界功法培养', () => { expectedRevision: state.revision, }; expect( - changeManual({ ...base, resources: { experience: 19, insight: 100 } }).ok, + changeManual({ ...base, resources: { experience: 39, insight: 100 } }).ok, ).toBe(false); expect( - changeManual({ ...base, resources: { experience: 20, insight: 1 } }).ok, + changeManual({ ...base, resources: { experience: 40, insight: 3 } }).ok, ).toBe(false); expect( changeManual({ ...base, expectedRevision: 0, - resources: { experience: 20, insight: 2 }, + resources: { experience: 40, insight: 4 }, }).ok, ).toBe(false); expect( - changeManual({ ...base, resources: { experience: 20, insight: 2 } }), - ).toMatchObject({ ok: true, cost: { experience: 20, insight: 2 } }); + changeManual({ ...base, resources: { experience: 40, insight: 4 } }), + ).toMatchObject({ ok: true, cost: { experience: 40, insight: 4 } }); expect(state.learned[0].level).toBe(1); }); it('拒绝伪造层数、提前解锁、重复槽位和未学激活', () => { @@ -138,15 +139,14 @@ describe('境界功法培养', () => { ]) expect(validateManualStateV1(invalid, '炼气').length).toBeGreaterThan(0); }); - it('双效果从一层生效,未激活功法不贡献,属性只在投影中累加一次', () => { - let state = success(act(empty(), 'learn', 'character_manual.songhe')); + it('属性与机制从一层生效,未激活功法不贡献,属性只在投影中累加一次', () => { + let state = success(act(empty(), 'learn', 'character_manual.qingmu')); state = success(act(state, 'learn')); const result = compileCharacterManualsV1({ state, realm: '炼气' }); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.projection.attributeBonuses).toEqual({ - vitality: 1, - willpower: 1, + willpower: 12, }); expect(result.projection.passiveSkillIds).toHaveLength(1); const character = { @@ -165,15 +165,15 @@ describe('境界功法培养', () => { }; const derived = withManualAttributes(character, result.projection); expect(character.attributes.vitality).toBe(10); - expect(compileCharacterPanelV1(derived.attributes).maxHp).toBe(488); - expect(compileCharacterPanelV1(derived.attributes).maxMp).toBe(305); + expect(compileCharacterPanelV1(derived.attributes).maxHp).toBe(480); + expect(compileCharacterPanelV1(derived.attributes).maxMp).toBe(360); }); }); describe('功法数据包', () => { - it('十六本内容与逐层成本可以加载', () => { - expect(loadManualPack(MANUAL_PACK).manuals).toHaveLength(16); + it('二十四本内容与逐层成本可以加载', () => { + expect(loadManualPack(MANUAL_PACK).manuals).toHaveLength(24); }); - it.each(['duplicate', 'reference', 'attribute', 'cost', 'bottleneck'])( + it.each(['duplicate', 'reference', 'attribute', 'initial', 'cost', 'bottleneck'])( '无效配置 %s 在加载时失败', (kind) => { const pack = structuredClone(MANUAL_PACK); @@ -181,6 +181,7 @@ describe('功法数据包', () => { if (kind === 'reference') pack.manuals[0].progressionId = 'missing'; if (kind === 'attribute') pack.manuals[0].effects.push(pack.manuals[0].effects[0]); + if (kind === 'initial') pack.manuals[0].effects[0].valueAt1 = 0; if (kind === 'cost') pack.progressions.standard.costsByRealm.炼气.pop(); if (kind === 'bottleneck') pack.progressions.standard.bottlenecks = [6, 3]; diff --git a/src/shared/engine/combat-v6/manuals/mechanism.test.ts b/src/shared/engine/combat-v6/manuals/mechanism.test.ts new file mode 100644 index 00000000..39d7e5bc --- /dev/null +++ b/src/shared/engine/combat-v6/manuals/mechanism.test.ts @@ -0,0 +1,435 @@ +import { describe, expect, it } from 'vitest'; +import { JIUJIE_COMBAT } from '../content/jiujie-pack'; +import { + CommandType, + DamageOrigin, + EffectType, + HookName, + SkillTag, + TargetSide, + createBattle, + type SkillDef, + type StatusDef, +} from '../core'; +import { createDaoyouRuleset } from '../rules-daoyou'; +import { COMBAT_V6_PHASE_1_VERSIONS } from '../version'; +import { compileCharacterManualsV1, manualSlot } from './compiler'; +import { CHARACTER_MANUALS_V1, MANUAL_PACK } from './content'; +import { compileManualSkill, manualMechanismValue } from './mechanism'; +import { loadManualPack } from './pack'; +import { manualEffectLines } from './presentation'; + +const statuses: StatusDef[] = [ + { id: 'dot', name: '毒', kind: 'dot', category: 'dot' }, + { id: 'debuff', name: '减益', kind: 'debuff', category: 'debuff' }, + { + id: 'control', + name: '封法', + kind: 'control', + category: 'control', + blocksSpell: true, + }, + { + id: 'revive', + name: '锢魂', + kind: 'revive', + category: 'control', + blocksRevive: true, + }, + { + id: 'locked', + name: '不可驱散', + kind: 'locked', + category: 'control', + dispellable: false, + }, +]; +const attack: SkillDef = { + id: 'strike', + name: '打击', + tags: [SkillTag.Physical], + targeting: { side: TargetSide.Enemy }, + effects: [{ type: EffectType.PhysicalHit }], +}; +function setup(ids: string[], level = 9, skill = attack) { + const passives = ids.map((id) => + compileManualSkill( + CHARACTER_MANUALS_V1.find((m) => m.id === `character_manual.${id}`)!, + level, + ), + ); + const session = createBattle({ + seed: 42, + versions: COMBAT_V6_PHASE_1_VERSIONS, + ruleset: createDaoyouRuleset({ + formulas: { + baseDamage: () => 1000, + physicalHitChance: () => 1, + spellHitChance: () => 1, + fluctuationMin: 1, + fluctuationMax: 1, + defendPhysicalFactor: 1, + }, + }), + skills: [skill, ...passives], + statusDefs: statuses, + units: [ + { + id: 'a', + name: 'a', + side: 0, + kind: 'player', + skills: [skill.id], + passives: passives.map((s) => s.id), + attrs: { + hp: 10000, + maxHp: 10000, + mp: 1000, + maxMp: 1000, + speed: 20, + physicalAtk: 100, + physicalDef: 0, + critRate: 0, + spellCritRate: 0, + }, + }, + { + id: 'b', + name: 'b', + side: 1, + kind: 'npc', + attrs: { + hp: 10000, + maxHp: 10000, + speed: 10, + physicalAtk: 1, + physicalDef: 0, + critRate: 0, + }, + }, + ], + }); + return session; +} +function status(session: ReturnType, id: string, unitId = 'a') { + const def = statuses.find((s) => s.id === id)!; + session.unit(unitId).statuses.push({ + id, + kind: def.kind, + sourceId: 'b', + remainingRounds: 2, + appliedRound: 0, + speedMod: 0, + attrMods: {}, + damageTakenPhysical: 1, + damageTakenSpell: 1, + healTaken: 1, + healDealt: 1, + stacks: 1, + }); +} +function barrier( + session: ReturnType, + unitId = 'a', + amount = 100, +) { + session.unit(unitId).barriers.push({ + id: 'shield', + kind: 'shield', + name: '盾', + current: amount, + remainingRounds: 2, + sourceId: 'a', + appliedRound: 0, + }); +} +function strike( + session: ReturnType, + kind: 'physical' | 'spell' | 'fixed', + defense = false, + extra = {}, +) { + return session.hooks.emit(HookName.OnHitCalc, { + source: session.unit(defense ? 'b' : 'a'), + target: session.unit(defense ? 'a' : 'b'), + kind, + damage: 1000, + origin: DamageOrigin.ActionDirect, + skillId: attack.id, + isPrimary: true, + ...extra, + }).damage!; +} + +describe('首版功法内容合同', () => { + it('24本各六种,六维与机制逐层成长且无品质/产出字段', () => { + for (const realm of ['炼气', '筑基', '金丹', '元婴']) + expect( + CHARACTER_MANUALS_V1.filter((m) => m.realm === realm), + ).toHaveLength(6); + for (const manual of CHARACTER_MANUALS_V1) { + expect(manual).not.toHaveProperty('rarity'); + expect(manual).not.toHaveProperty('dropWeight'); + expect(manual.effects).toHaveLength(1); + for (let level = 1; level <= 9; level++) { + const result = compileCharacterManualsV1({ + realm: '元婴', + state: { + version: 1, + revision: 0, + learned: [ + { + manualId: manual.id, + level, + unlockedLevel: level <= 3 ? 3 : level <= 6 ? 6 : 9, + }, + ], + build: { + slots: [{ slot: manualSlot(manual), manualId: manual.id }], + }, + }, + }); + expect(result.ok).toBe(true); + if (!result.ok) continue; + expect( + result.projection.attributeBonuses[manual.effects[0].attribute], + ).toBe( + manual.effects[0].valueAt1 + + (level - 1) * manual.effects[0].valuePerLevel, + ); + expect(manualEffectLines(manual, level)).toHaveLength(2); + expect(manualMechanismValue(manual.mechanism, level)).toBeGreaterThan( + level === 1 ? 0 : manualMechanismValue(manual.mechanism, level - 1), + ); + } + } + }); + it('拒绝旧稀有度字段和倒退成长', () => { + expect(() => + loadManualPack({ + ...MANUAL_PACK, + manuals: [{ ...MANUAL_PACK.manuals[0], rarity: 'rare' }], + }), + ).toThrow(); + const data = structuredClone(MANUAL_PACK); + data.manuals[0].mechanism.valueAt9 = 0.0001; + expect(() => loadManualPack(data)).toThrow(); + }); + it('拒绝不成长的机制与回合恢复的目标条件', () => { + const data = structuredClone(MANUAL_PACK); + data.manuals[0].mechanism.valueAt9 = data.manuals[0].mechanism.valueAt1; + expect(() => loadManualPack(data)).toThrow(); + data.manuals[0].mechanism.valueAt9 = 0.01; + data.manuals[0].mechanism.condition = 'targetHpBelow50'; + expect(() => loadManualPack(data)).toThrow(); + }); + it('大衍的每层概率差值保留小数面板点数', () => { + const result = compileCharacterManualsV1({ + realm: '筑基', + state: { + version: 1, + revision: 0, + learned: [ + { manualId: 'character_manual.dayan', level: 2, unlockedLevel: 3 }, + ], + build: { slots: [{ slot: 2, manualId: 'character_manual.dayan' }] }, + }, + }); + expect(result.ok && result.projection.panel).toEqual([ + { attr: 'sealResist', mode: 'add', value: 2.5 }, + ]); + }); +}); + +describe('单条件机制结算', () => { + it.each([ + ['gengjin', 1030], + ['qingyuan', 1040], + ] as const)( + '%s只强化主目标直接攻击,排除派生、比例扣血及器诀', + (id, damage) => { + const s = setup([id]); + expect(strike(s, 'physical')).toBeCloseTo(damage); + expect(strike(s, 'physical', false, { isPrimary: false })).toBe(1000); + expect( + strike(s, 'physical', false, { origin: DamageOrigin.HookDerived }), + ).toBe(1000); + expect(strike(s, 'physical', false, { percentageDamage: true })).toBe( + 1000, + ); + const art = setup([id], 9, { + ...attack, + tags: [SkillTag.Physical, SkillTag.Art], + }); + expect(strike(art, 'physical')).toBe(1000); + }, + ); + it('血量阈值严格,多段跨过70%后停止增伤', () => { + const s = setup(['gengjin'], 9, { + ...attack, + effects: [{ type: EffectType.PhysicalHit, hits: 2 }], + }); + s.unit('b').attrs.hp = 7500; + s.submit('a', { + type: CommandType.Skill, + skillId: attack.id, + targets: ['b'], + }); + s.submit('b', { type: CommandType.Defend }); + s.lockAndResolve(); + expect(s.unit('b').attrs.hp).toBe(5470); + const exact = setup(['gengjin']); + exact.unit('b').attrs.hp = 7000; + expect(strike(exact, 'physical')).toBe(1000); + }); + it.each(['shayao', 'liuji', 'yousha'])('%s只在对应残血条件增伤', (id) => { + const s = setup([id]); + expect(strike(s, 'physical')).toBe(1000); + s.unit(id === 'yousha' ? 'b' : 'a').attrs.hp = 3500; + expect(strike(s, 'physical')).toBe(1000); + s.unit(id === 'yousha' ? 'b' : 'a').attrs.hp = 3499; + expect(strike(s, 'physical')).toBeCloseTo(id === 'shayao' ? 1040 : 1060); + expect(strike(s, 'fixed')).toBeCloseTo( + id === 'liuji' ? 1000 : id === 'shayao' ? 1040 : 1060, + ); + }); + it('法力条件与法术加成区分气血和法力', () => { + const s = setup(['sanzhuan', 'chiyan', 'taiyang']); + expect(strike(s, 'spell')).toBeCloseTo(1000 * 1.04 * 1.03 * 1.06); + s.unit('a').attrs.mp = 700; + expect(strike(s, 'spell')).toBeCloseTo(1000 * 1.03 * 1.06); + s.unit('a').attrs.hp = 7000; + expect(strike(s, 'spell')).toBe(1000); + }); + it('有效护盾与防御条件,零盾不触发', () => { + const s = setup(['guiyuan', 'houtu_jue', 'taibai']); + expect(strike(s, 'physical', true)).toBe(1000); + barrier(s, 'a'); + barrier(s, 'b'); + expect(strike(s, 'physical', true)).toBeCloseTo(940); + expect(strike(s, 'physical')).toBeCloseTo(1060); + s.unit('a').flags.defending = true; + expect(strike(s, 'physical', true)).toBeCloseTo(940 * 0.91); + s.unit('b').barriers[0].current = 0; + expect(strike(s, 'physical')).toBe(1000); + }); + it('玄阴不强化持续伤害,减益与持续伤害不能混用', () => { + const s = setup(['xuanyin']); + status(s, 'debuff', 'b'); + expect(strike(s, 'fixed')).toBe(1000); + status(s, 'dot', 'b'); + expect(strike(s, 'fixed')).toBeCloseTo(1060); + expect(strike(s, 'spell')).toBeCloseTo(1060); + expect(strike(s, 'physical')).toBe(1000); + expect(strike(s, 'fixed', false, { origin: DamageOrigin.Status })).toBe( + 1000, + ); + }); + it('托天、元磁、明王和浩然分别识别条件', () => { + const s = setup(['tuotian', 'yuanci', 'mingwang', 'haoran']); + expect(strike(s, 'physical', true)).toBe(1000); + expect(strike(s, 'spell', true)).toBeCloseTo(930); + s.unit('a').attrs.mp = 500; + expect(strike(s, 'spell', true)).toBe(1000); + s.unit('a').attrs.hp = 4999; + expect(strike(s, 'physical', true)).toBeCloseTo(930); + status(s, 'revive'); + status(s, 'locked'); + expect(strike(s, 'spell', true)).toBe(1000); + status(s, 'control'); + expect(strike(s, 'spell', true)).toBeCloseTo(900); + status(s, 'debuff'); + expect(strike(s, 'spell', true)).toBeCloseTo(810); + }); + it('幻灵/千浪为概率差额,千浪只在半血以下生效', () => { + const s = setup(['huanling', 'qianlang']); + const roll = () => + s.hooks.emit(HookName.OnHitRoll, { + source: s.unit('b'), + target: s.unit('a'), + chance: 0.95, + kind: 'physical', + origin: DamageOrigin.ActionDirect, + }).chance; + expect(roll()).toBeCloseTo(0.92); + s.unit('a').attrs.hp = 4999; + expect(roll()).toBeCloseTo(0.86); + }); + it('治疗/护盾只增强低血主目标,不增强派生恢复', () => { + const s = setup(['qingmu', 'yudan', 'tianluo']); + s.unit('b').attrs.hp = 4999; + const base = { + source: s.unit('a'), + target: s.unit('b'), + skillId: attack.id, + isPrimary: true, + origin: DamageOrigin.ActionDirect, + }; + expect( + s.hooks.emit(HookName.OnHealCalc, { ...base, heal: 1000 }).heal, + ).toBeCloseTo(1220.8); + expect( + s.hooks.emit(HookName.OnBarrierCalc, { ...base, barrier: 1000 }).barrier, + ).toBeCloseTo(1150); + expect( + s.hooks.emit(HookName.OnHealCalc, { + ...base, + heal: 1000, + isPrimary: false, + }).heal, + ).toBe(1000); + expect( + s.hooks.emit(HookName.OnHealCalc, { + ...base, + heal: 1000, + origin: DamageOrigin.HookDerived, + }).heal, + ).toBe(1000); + }); + it('回合恢复受半血/半蓝、伤势和存活限制,且向下取整', () => { + const s = setup(['changchun', 'qingquan', 'qingmu', 'yudan']); + s.unit('a').attrs.hp = 4990; + s.unit('a').attrs.mp = 490; + s.hooks.emit(HookName.OnRoundEnd); + expect(s.unit('a').attrs.hp).toBe(5065); + expect(s.unit('a').attrs.mp).toBe(497); + s.unit('a').wound = 5100; + s.unit('a').attrs.hp = 4800; + s.hooks.emit(HookName.OnRoundEnd); + s.hooks.emit(HookName.OnRoundEnd); + expect(s.unit('a').attrs.hp).toBe(4900); + s.unit('a').flags.downed = true; + s.unit('a').attrs.hp = 0; + s.hooks.emit(HookName.OnRoundEnd); + expect(s.unit('a').attrs.hp).toBe(0); + }); + it.each([false, true])('实际行动保留比例固伤分类:%s', (percentageDamage) => { + const s = setup(['yousha'], 9, { + ...attack, + effects: [{ type: EffectType.FixedHit, power: 1000, percentageDamage }], + }); + s.unit('b').attrs.hp = 3000; + s.submit('a', { + type: CommandType.Skill, + skillId: attack.id, + targets: ['b'], + }); + s.submit('b', { type: CommandType.Defend }); + s.lockAndResolve(); + expect(s.unit('b').attrs.hp).toBe(percentageDamage ? 2000 : 1940); + }); + it('五雷的所有百分比伤害分支有显式分类,保持原有其他效果', () => { + const skill = JIUJIE_COMBAT.skills.find((s) => + s.definition.effects.some((e) => e.type === EffectType.RandomBranch), + )!.definition; + for (const effect of skill.effects) + if (effect.type === EffectType.RandomBranch) { + for (const branch of [ + ...effect.successEffects, + ...effect.failureEffects, + ]) + if (branch.type === EffectType.FixedHit) + expect(branch.percentageDamage).toBe(true); + } + }); +}); diff --git a/src/shared/engine/combat-v6/manuals/mechanism.ts b/src/shared/engine/combat-v6/manuals/mechanism.ts new file mode 100644 index 00000000..4fb229f1 --- /dev/null +++ b/src/shared/engine/combat-v6/manuals/mechanism.ts @@ -0,0 +1,166 @@ +import { + DamageOrigin, + EffectType, + HookAim, + HookName, + SkillTag, + StatusCategory, + TargetSide, + type EffectWhen, + type SkillDef, + type SkillHook, +} from '../core'; +import type { ManualMechanism } from './pack'; + +export function manualMechanismValue( + mechanism: ManualMechanism, + level: number, +): number { + return Number( + ( + mechanism.valueAt1 + + ((mechanism.valueAt9 - mechanism.valueAt1) * (level - 1)) / 8 + ).toFixed(8), + ); +} + +function conditionWhen(condition: ManualMechanism['condition']): EffectWhen { + switch (condition) { + case 'always': + return {}; + case 'selfHpBelow50': + return { sourceHpRatioBelow: 0.5 }; + case 'selfHpBelow35': + return { sourceHpRatioBelow: 0.35 }; + case 'selfHpAbove70': + return { sourceHpRatioAbove: 0.7 }; + case 'targetHpBelow50': + return { targetHpRatioBelow: 0.5 }; + case 'targetHpBelow35': + return { targetHpRatioBelow: 0.35 }; + case 'targetHpAbove70': + return { targetHpRatioAbove: 0.7 }; + case 'selfMpBelow50': + return { sourceMpRatioBelow: 0.5 }; + case 'selfMpAbove50': + return { sourceMpRatioAbove: 0.5 }; + case 'selfMpAbove70': + return { sourceMpRatioAbove: 0.7 }; + case 'defending': + return { sourceDefending: true }; + case 'selfBarrier': + return { sourceHasBarrier: true }; + case 'targetBarrier': + return { targetHasBarrier: true }; + case 'targetDot': + return { targetStatusCategories: [StatusCategory.Dot] }; + case 'selfControl': + return { sourceRemovableControl: true }; + case 'selfDebuff': + return { sourceStatusCategories: [StatusCategory.Debuff] }; + } +} + +export function compileManualSkill( + manual: { id: string; name: string; mechanism: ManualMechanism }, + level: number, +): SkillDef { + const mechanism = manual.mechanism; + const value = manualMechanismValue(mechanism, level); + const when: EffectWhen = { + ...conditionWhen(mechanism.condition), + sourceStanding: true, + }; + const direct: EffectWhen = { + ...when, + damageOrigins: [DamageOrigin.ActionDirect], + }; + const active: EffectWhen = { + ...direct, + excludeSkillTags: [SkillTag.Art, SkillTag.Passive], + targetSlot: 'primary', + excludePercentageDamage: true, + }; + let hooks: SkillHook[] = []; + switch (mechanism.type) { + case 'damage': + case 'mitigation': + hooks = mechanism.kinds.map((kind) => ({ + on: HookName.OnHitCalc, + ...(mechanism.type === 'damage' + ? { sourceIsSelf: true } + : { targetIsSelf: true }), + requireKind: kind, + when: mechanism.type === 'damage' ? active : direct, + effects: [ + { + type: EffectType.ModifyStrike, + factor: mechanism.type === 'damage' ? 1 + value : 1 - value, + }, + ], + })); + break; + case 'heal': + case 'barrier': + hooks = [ + { + on: + mechanism.type === 'heal' + ? HookName.OnHealCalc + : HookName.OnBarrierCalc, + sourceIsSelf: true, + when: active, + effects: [ + { + type: + mechanism.type === 'heal' + ? EffectType.ModifyHeal + : EffectType.ModifyBarrier, + factor: 1 + value, + }, + ], + }, + ]; + break; + case 'evasion': + hooks = [ + { + on: HookName.OnHitRoll, + targetIsSelf: true, + requireKind: 'physical', + when: direct, + effects: [{ type: EffectType.ModifyChance, add: -value }], + }, + ]; + break; + case 'restoreHp': + case 'restoreMp': + hooks = [ + { + on: HookName.OnRoundEnd, + aim: HookAim.Self, + when, + effects: [ + { + type: + mechanism.type === 'restoreHp' + ? EffectType.RestoreHp + : EffectType.RestoreMp, + power: `floor(source.${mechanism.type === 'restoreHp' ? 'maxHp' : 'maxMp'} * ${value})`, + }, + ], + }, + ]; + break; + case 'sealResist': + break; // Compiled as ruleset-scaled panel points, before seal caps. + } + return { + id: `${manual.id}.passive`, + name: manual.name, + tags: [SkillTag.Passive], + targeting: { side: TargetSide.Self }, + effects: [], + hooks, + }; +} diff --git a/src/shared/engine/combat-v6/manuals/pack.ts b/src/shared/engine/combat-v6/manuals/pack.ts index 66a0142e..2088bcdf 100644 --- a/src/shared/engine/combat-v6/manuals/pack.ts +++ b/src/shared/engine/combat-v6/manuals/pack.ts @@ -14,9 +14,64 @@ const cost = z.strictObject({ experience: positive, insight: z.number().int().min(1).max(100), }); +const growth = { + valueAt1: z.number().positive().max(0.2), + valueAt9: z.number().positive().max(0.2), +}; +export const ManualConditionShape = z.enum([ + 'always', + 'selfHpBelow50', + 'selfHpBelow35', + 'selfHpAbove70', + 'targetHpBelow50', + 'targetHpBelow35', + 'targetHpAbove70', + 'selfMpBelow50', + 'selfMpAbove50', + 'selfMpAbove70', + 'defending', + 'selfBarrier', + 'targetBarrier', + 'targetDot', + 'selfControl', + 'selfDebuff', +]); +export const ManualMechanismShape = z.discriminatedUnion('type', [ + z.strictObject({ + type: z.literal('damage'), + condition: ManualConditionShape, + kinds: z + .array(z.enum(['physical', 'spell', 'fixed'])) + .min(1) + .max(3), + ...growth, + }), + z.strictObject({ + type: z.literal('mitigation'), + condition: ManualConditionShape, + kinds: z + .array(z.enum(['physical', 'spell'])) + .min(1) + .max(2), + ...growth, + }), + z.strictObject({ + type: z.enum([ + 'heal', + 'barrier', + 'evasion', + 'sealResist', + 'restoreHp', + 'restoreMp', + ]), + condition: ManualConditionShape, + ...growth, + }), +]); +export type ManualMechanism = z.infer; export const ManualPackShape = z.strictObject({ $schema: z.string().optional(), - version: z.literal(1), + version: z.literal(3), progressions: z.record( z.string().min(1), z.strictObject({ @@ -31,19 +86,19 @@ export const ManualPackShape = z.strictObject({ id: z.string().regex(/^character_manual\.[a-z][a-z0-9_-]*$/), name: z.string().min(1), realm: z.enum(MANUAL_REALMS), - rarity: z.enum(['common', 'rare']), description: z.string().min(1), progressionId: z.string().min(1), - dropWeight: positive, effects: z .array( z.strictObject({ attribute: z.enum(MANUAL_ATTRIBUTES), + valueAt1: z.number().int().min(1).max(1000), valuePerLevel: z.number().int().min(1).max(100), }), ) .min(1) - .max(2), + .max(1), + mechanism: ManualMechanismShape, }), ) .min(1), @@ -55,6 +110,23 @@ export const ManualPackSchema = ManualPackShape.superRefine((pack, ctx) => { pack.manuals.forEach((manual, i) => { if (ids.has(manual.id)) issue(['manuals', i, 'id'], '功法 ID 重复'); ids.add(manual.id); + const mechanism = manual.mechanism; + if (mechanism.valueAt9 <= mechanism.valueAt1) + issue(['manuals', i, 'mechanism'], '机制必须随层数增强'); + if ( + 'kinds' in mechanism && + new Set(mechanism.kinds).size !== mechanism.kinds.length + ) + issue(['manuals', i, 'mechanism'], '伤害类型重复'); + if ( + ['mitigation', 'evasion', 'restoreHp', 'restoreMp'].includes( + mechanism.type, + ) && + mechanism.condition.startsWith('target') + ) + issue(['manuals', i, 'mechanism'], '防护与回合恢复仅支持自身条件'); + if (mechanism.type === 'sealResist' && mechanism.condition !== 'always') + issue(['manuals', i, 'mechanism'], '封印抵抗为常驻能力'); if (!pack.progressions[manual.progressionId]) issue(['manuals', i, 'progressionId'], '培养规则不存在'); if ( diff --git a/src/shared/engine/combat-v6/manuals/presentation.ts b/src/shared/engine/combat-v6/manuals/presentation.ts new file mode 100644 index 00000000..2b19379c --- /dev/null +++ b/src/shared/engine/combat-v6/manuals/presentation.ts @@ -0,0 +1,63 @@ +import { CHARACTER_ATTRIBUTE_LABELS } from '@shared/lib/characterAttributeLabels'; +import { manualAttributeValue } from './attributes'; +import { manualMechanismValue } from './mechanism'; +import type { ManualMechanism } from './pack'; +import type { CharacterManualDefV1 } from './types'; + +const conditions: Record = { + always: '', + selfHpBelow50: '自身气血低于50%时,', + selfHpBelow35: '自身气血低于35%时,', + selfHpAbove70: '自身气血高于70%时,', + targetHpBelow50: '目标气血低于50%时,', + targetHpBelow35: '目标气血低于35%时,', + targetHpAbove70: '目标气血高于70%时,', + selfMpBelow50: '自身法力低于50%时,', + selfMpAbove50: '自身法力高于50%时,', + selfMpAbove70: '自身法力高于70%时,', + defending: '自身防御时,', + selfBarrier: '自身有护盾时,', + targetBarrier: '目标有护盾时,', + targetDot: '目标带有持续伤害状态时,', + selfControl: '自身处于可解除封印时,', + selfDebuff: '自身带有减益状态时,', +}; +const damageLabels = { physical: '物理', spell: '法术', fixed: '普通固定' }; +export function manualMechanismDescription( + manual: CharacterManualDefV1, + level: number, +): string { + const effect = manual.mechanism; + const value = Number((manualMechanismValue(effect, level) * 100).toFixed(6)); + const prefix = conditions[effect.condition]; + switch (effect.type) { + case 'damage': + return `${prefix}普攻、宗门主动技能对主目标的直接${effect.kinds.map((k) => damageLabels[k]).join('、')}伤害+${value}%`; + case 'mitigation': + return `${prefix}受到的直接${effect.kinds.map((k) => damageLabels[k]).join('、')}伤害-${value}%`; + case 'heal': + return `${prefix}宗门主动技能对主目标的治疗量+${value}%`; + case 'barrier': + return `${prefix}宗门主动技能对主目标的护盾量+${value}%`; + case 'evasion': + return `${prefix}直接物理攻击的命中概率-${value}%(概率差值)`; + case 'sealResist': + return `受到封印时,对方封印成功概率-${value}%(概率差值,遵守封印上下限)`; + case 'restoreHp': + return `回合结束时,${prefix}恢复最大气血的${value}%`; + case 'restoreMp': + return `回合结束时,${prefix}恢复最大法力的${value}%`; + } +} +export function manualEffectLines( + manual: CharacterManualDefV1, + level: number, +): string[] { + return [ + ...manual.effects.map( + (e) => + `${CHARACTER_ATTRIBUTE_LABELS[e.attribute]} +${manualAttributeValue(e, level)}`, + ), + manualMechanismDescription(manual, level), + ]; +} diff --git a/src/shared/engine/combat-v6/manuals/state.ts b/src/shared/engine/combat-v6/manuals/state.ts index d3c2e2ee..2afdecad 100644 --- a/src/shared/engine/combat-v6/manuals/state.ts +++ b/src/shared/engine/combat-v6/manuals/state.ts @@ -49,7 +49,7 @@ export function changeManual(input: { ), ).length; if (count >= MAX_MANUALS_PER_SLOT) - return fail('该境界位已学满三种功法,暂不支持遗忘或学习新的功法'); + return fail('该境界位已学满六种功法'); next.learned.push({ manualId: def.id, level: 1, diff --git a/src/shared/engine/combat-v6/manuals/types.ts b/src/shared/engine/combat-v6/manuals/types.ts index 1c9b8c03..537085b1 100644 --- a/src/shared/engine/combat-v6/manuals/types.ts +++ b/src/shared/engine/combat-v6/manuals/types.ts @@ -32,13 +32,13 @@ export interface CombatV6CapabilityContribution { export interface CharacterManualDefV1 { id: string; realm: '炼气' | '筑基' | '金丹' | '元婴'; - rarity: 'common' | 'rare'; name: string; description: string; progressionId: string; - dropWeight: number; + mechanism: import('./pack').ManualMechanism; effects: Array<{ attribute: keyof import('@shared/types/cultivator').Attributes; + valueAt1: number; valuePerLevel: number; }>; skill: SkillDef; diff --git a/src/shared/engine/combat-v6/projection/compose-character-manuals.ts b/src/shared/engine/combat-v6/projection/compose-character-manuals.ts index 5f047b84..821f7576 100644 --- a/src/shared/engine/combat-v6/projection/compose-character-manuals.ts +++ b/src/shared/engine/combat-v6/projection/compose-character-manuals.ts @@ -30,7 +30,9 @@ export function composeCharacterManuals( const attrs = { ...base.unit.attrs } for (const contribution of manuals.projection.panel) { attrs[contribution.attr] = contribution.mode === "add" - ? Math.floor((attrs[contribution.attr] ?? 0) + contribution.value) + ? (contribution.attr === 'sealResist' + ? (attrs[contribution.attr] ?? 0) + contribution.value + : Math.floor((attrs[contribution.attr] ?? 0) + contribution.value)) : Math.floor((attrs[contribution.attr] ?? 0) * contribution.value) } const passiveIds = new Set(capabilities.passiveIds) diff --git a/src/shared/engine/combat-v6/projection/fixtures/before-a1.json b/src/shared/engine/combat-v6/projection/fixtures/before-a1.json index aa5893ab..ece34efc 100644 --- a/src/shared/engine/combat-v6/projection/fixtures/before-a1.json +++ b/src/shared/engine/combat-v6/projection/fixtures/before-a1.json @@ -1253,9 +1253,9 @@ "3cc7408629789c6f1cca943d940341721c5f9522f62517e035c1f2a8dd6b391c", "a4364fe8654b80b97c8f79d404b8e9d5571928378b4d610cfde476acdd472dc9", "3cc7408629789c6f1cca943d940341721c5f9522f62517e035c1f2a8dd6b391c", - "219d2a207d1ad62fb2457f28247c1e9a95d0b4c60bfb4f0dfa68c2546e04c43d", + "dbc3f5435d3f12b38bf20454767a254a3ecf3c201be3173bfbbb3aa1ae7b9ca2", "3cc7408629789c6f1cca943d940341721c5f9522f62517e035c1f2a8dd6b391c", - "8bf817269ef20247852612920390c90593d3f7c569cb8bb79123703236ba26f5", + "c756dfbb95211771b11066aac8cd19b82eae870bfbef7af605be9a92d77d5ca7", "3cc7408629789c6f1cca943d940341721c5f9522f62517e035c1f2a8dd6b391c" ] } diff --git a/src/shared/manuals/action.test.ts b/src/shared/manuals/action.test.ts index a7a7a5d9..92d3367c 100644 --- a/src/shared/manuals/action.test.ts +++ b/src/shared/manuals/action.test.ts @@ -3,10 +3,11 @@ import { ManualActionSchema, type ManualAction, } from '../contracts/combatV6Manuals'; +import { MANUAL_PACK } from '../engine/combat-v6/manuals/content'; import type { CultivatorManualStateV1 } from '../engine/combat-v6/manuals/types'; import type { InventoryItem } from '../inventory'; import { QINGXI_POOL_V2 } from '../rewards/wild'; -import { previewManualAction } from './action'; +import { manualJadeCost, previewManualAction } from './action'; const state: CultivatorManualStateV1 = { version: 1, @@ -33,6 +34,56 @@ const action: ManualAction = { item: { id: 'jade', revision: 0 }, }; const resources = { experience: 0, insight: 0 }; +it.each([ + ['炼气', 'changchun', 1, 320, 18, 1440, 88], + ['筑基', 'guiyuan', 2, 1600, 27, 7200, 132], + ['金丹', 'taibai', 3, 8000, 36, 36000, 176], + ['元婴', 'tuotian', 4, 32000, 45, 144000, 220], +] as const)( + '%s费用按功法境界计算,预览遵守精确余额和最新总预算', + (realm, key, slot, experience, insight, totalExperience, totalInsight) => { + const manualId = `character_manual.${key}`; + const learned: CultivatorManualStateV1 = { + version: 1, + revision: 0, + learned: [{ manualId, level: 8, unlockedLevel: 9 }], + build: { slots: [{ slot, manualId }] }, + }; + const action: ManualAction = { + action: 'train', + manualId, + slot, + expectedRevision: 0, + }; + expect( + previewManualAction(learned, '元婴', action, { experience, insight }), + ).toMatchObject({ + ok: true, + cost: { experience, insight }, + state: { learned: [{ level: 9 }] }, + }); + expect( + previewManualAction(learned, '元婴', action, { + experience: experience - 1, + insight, + }).ok, + ).toBe(false); + expect( + previewManualAction(learned, '元婴', action, { + experience, + insight: insight - 1, + }).ok, + ).toBe(false); + expect(learned.learned[0].level).toBe(8); + const costs = MANUAL_PACK.progressions.standard.costsByRealm[realm]; + expect(costs.reduce((sum, cost) => sum + cost.experience, 0)).toBe( + totalExperience, + ); + expect(costs.reduce((sum, cost) => sum + cost.insight, 0)).toBe( + totalInsight, + ); + }, +); it('学习仅接受储物袋中版本一致的同名玉简', () => { expect(previewManualAction(state, '炼气', action, resources, item).ok).toBe( true, @@ -41,7 +92,7 @@ it('学习仅接受储物袋中版本一致的同名玉简', () => { undefined, { ...item, revision: 1 }, { ...item, quantity: 0 }, - { ...item, definitionId: 'jade.character_manual.songhe' }, + { ...item, definitionId: 'jade.character_manual.qingmu' }, { ...item, location: 'warehouse' as InventoryItem['location'] }, ]) expect(previewManualAction(state, '炼气', action, resources, bad).ok).toBe( @@ -61,36 +112,62 @@ it('严格请求拒绝旧改修协议、非法槽位和客户端自报费用', ( ManualActionSchema.safeParse({ ...action, action: 'forget' }).success, ).toBe(false); }); -it('同名瓶颈玉简校验与学习一致', () => { - const learned: CultivatorManualStateV1 = { - version: 1, - revision: 1, - learned: [{ manualId, level: 3, unlockedLevel: 3 }], - build: { slots: [{ slot: 1, manualId }] }, - }; - const unlock = { ...action, action: 'unlock' as const, expectedRevision: 1 }; - expect( - previewManualAction(learned, '炼气', unlock, resources, { - ...item, - definitionId: 'jade.character_manual.songhe', - }).ok, - ).toBe(false); - expect( - previewManualAction(learned, '炼气', unlock, resources, item), - ).toMatchObject({ - ok: true, - state: { learned: [{ level: 3, unlockedLevel: 6 }] }, - }); +it.each([ + [3, 2, 6], + [6, 3, 9], +] as const)( + '%s层突破需%s本,数量不足或版本过期不解锁', + (level, quantity, unlockedLevel) => { + const learned: CultivatorManualStateV1 = { + version: 1, + revision: 1, + learned: [{ manualId, level, unlockedLevel: level }], + build: { slots: [{ slot: 1, manualId }] }, + }; + const unlock = { + ...action, + action: 'unlock' as const, + expectedRevision: 1, + }; + expect(manualJadeCost(learned, unlock)).toBe(quantity); + for (const bad of [ + { ...item, quantity: quantity - 1 }, + { ...item, quantity, definitionId: 'jade.character_manual.qingmu' }, + { ...item, quantity, revision: 1 }, + { ...item, quantity, location: 'warehouse' as InventoryItem['location'] }, + ]) + expect( + previewManualAction(learned, '炼气', unlock, resources, bad).ok, + ).toBe(false); + for (const count of [quantity, quantity + 1]) { + expect( + previewManualAction(learned, '炼气', unlock, resources, { + ...item, + quantity: count, + }), + ).toMatchObject({ + ok: true, + cost: { experience: 0, insight: 0 }, + state: { learned: [{ level, unlockedLevel }] }, + }); + } + expect(learned.learned[0].unlockedLevel).toBe(level); + }, +); +it('学习消耗一本,修炼与切换不额外消耗玉简', () => { + expect(manualJadeCost(state, action)).toBe(1); + expect(manualJadeCost(state, { ...action, action: 'train' })).toBe(0); + expect(manualJadeCost(state, { ...action, action: 'activate' })).toBe(0); }); -it('常见与稀有玉简在既有掉落池采用数据包权重', () => { +it('玉简由奖励包显式等权投放,不依赖稀有度', () => { const entries = QINGXI_POOL_V2.groups.find( (g) => g.id === 'manuals', )!.entries; - expect(entries).toHaveLength(16); + expect(entries).toHaveLength(24); expect(entries.find((e) => e.rewardId === item.definitionId)!.weight).toBe( 100, ); expect( - entries.find((e) => e.rewardId === 'jade.character_manual.songhe')!.weight, - ).toBe(5); + entries.find((e) => e.rewardId === 'jade.character_manual.qingmu')!.weight, + ).toBe(100); }); diff --git a/src/shared/manuals/action.ts b/src/shared/manuals/action.ts index 303e6224..641a3be7 100644 --- a/src/shared/manuals/action.ts +++ b/src/shared/manuals/action.ts @@ -1,4 +1,8 @@ import type { ManualAction } from '../contracts/combatV6Manuals'; +import { + CHARACTER_MANUALS_V1, + manualRule, +} from '../engine/combat-v6/manuals/content'; import { changeManual } from '../engine/combat-v6/manuals/state'; import type { CultivatorManualStateV1, @@ -8,6 +12,20 @@ import type { InventoryItem } from '../inventory'; import { findItemDefinition } from '../items/registry'; import type { RealmType } from '../types/constants'; +/** Learning costs one jade; successive bottlenecks cost two, then three. */ +export function manualJadeCost( + state: CultivatorManualStateV1, + action: Pick, +): number { + if (action.action === 'learn') return 1; + if (action.action !== 'unlock') return 0; + const manual = CHARACTER_MANUALS_V1.find((m) => m.id === action.manualId); + const learned = state.learned.find((m) => m.manualId === action.manualId); + if (!manual || !learned) return 0; + const index = manualRule(manual).bottlenecks.indexOf(learned.level); + return index < 0 ? 0 : index + 2; +} + /** Shared preview and authoritative validation, before any resource or inventory mutation. */ export function previewManualAction( state: CultivatorManualStateV1, @@ -16,13 +34,14 @@ export function previewManualAction( resources: { experience: number; insight: number }, item?: InventoryItem, ): ManualStateChangeResult { + const jadeCost = manualJadeCost(state, action); if ( 'item' in action && (!item || item.id !== action.item.id || item.revision !== action.item.revision || item.location !== 'bag' || - item.quantity < 1 || + item.quantity < jadeCost || findItemDefinition(item.definitionId)?.manualId !== action.manualId) ) { return { @@ -31,7 +50,7 @@ export function previewManualAction( { severity: 'error', code: 'INVALID_MANUAL_STATE', - message: '需要储物袋中的同名功法玉简,请刷新核对', + message: `需要储物袋中数量足够的同名功法玉简(本次 ${jadeCost} 本),请刷新核对`, }, ], }; diff --git a/src/shared/rewards/data/README.md b/src/shared/rewards/data/README.md index 9e74fabe..dbd2a00f 100644 --- a/src/shared/rewards/data/README.md +++ b/src/shared/rewards/data/README.md @@ -11,8 +11,8 @@ - 每组 `chance` 为独立掉落概率,`fixed` 条目维护注册物品 ID、权重和数量上下界。 - `equipment`/`blueprints` 按 `equipmentLevels` 和有序 `slots` 生成装备/图纸条目;装备一次只能产出一件,图纸可配置固定条目的数量。 -- `manualJades` 从角色功法目录生成玉简条目,并使用功法包的 `dropWeight`,不另复制稀有度权重。 -- 区域引用、等级/部位/条目/组重复、未知奖励及非法数量在加载时拒绝。奖励组身份和池版本保持既有值;版本不等于数据包的 `contentRevision`。 +- 功法玉简使用 `fixed` 显式配置:首版 24 本各权重 100、组概率 3%;功法包不存产出权重或稀有度。 +- 区域引用、等级/部位/条目/组重复、未知奖励及非法数量在加载时拒绝。奖励组身份保持既有值;调整条目时同步递增池版本;版本不等于数据包的 `contentRevision`。 - 加载和编译不抽随机数,物品生成继续由既有掉落与铸造函数负责。 `dungeon.json` 定义副本探索、战斗、通关三种来源的奖励。 diff --git a/src/shared/rewards/data/wild.json b/src/shared/rewards/data/wild.json index aa92b30b..01a1fca9 100644 --- a/src/shared/rewards/data/wild.json +++ b/src/shared/rewards/data/wild.json @@ -1,10 +1,10 @@ { "$schema": "./wild.schema.json", "formatVersion": 1, - "contentRevision": 11, + "contentRevision": 12, "nodeId": "SAT_TN_08", "poolId": "qingxi", - "poolVersion": 12, + "poolVersion": 13, "equipmentLevels": [ 10 ], @@ -629,7 +629,201 @@ "id": "manuals", "chance": 0.03, "source": { - "kind": "manualJades" + "kind": "fixed", + "entries": [ + { + "rewardId": "jade.character_manual.changchun", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.gengjin", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.qingmu", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.qingquan", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.chiyan", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.houtu_jue", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.guiyuan", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.huanling", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.qingyuan", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.dayan", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.sanzhuan", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.shayao", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.taibai", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.taiyang", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.xuanyin", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.yudan", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.tianluo", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.liuji", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.tuotian", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.mingwang", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.yuanci", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.haoran", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.qianlang", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + }, + { + "rewardId": "jade.character_manual.yousha", + "weight": 100, + "quantity": { + "min": 1, + "max": 1 + } + } + ] } } ] diff --git a/src/shared/rewards/data/wild.schema.json b/src/shared/rewards/data/wild.schema.json index 79a238c5..ffe3c74f 100644 --- a/src/shared/rewards/data/wild.schema.json +++ b/src/shared/rewards/data/wild.schema.json @@ -117,19 +117,6 @@ "slots" ], "additionalProperties": false - }, - { - "type": "object", - "properties": { - "kind": { - "type": "string", - "const": "manualJades" - } - }, - "required": [ - "kind" - ], - "additionalProperties": false } ] } diff --git a/src/shared/rewards/wild-pack.ts b/src/shared/rewards/wild-pack.ts index bc993efa..a3254a34 100644 --- a/src/shared/rewards/wild-pack.ts +++ b/src/shared/rewards/wild-pack.ts @@ -3,9 +3,7 @@ import { formatContentPackErrors } from '@shared/lib/content-pack-errors'; import { z } from 'zod'; import { DropPoolSchema, type DropPool } from '../drops'; import { DAO_EQUIPMENT_SLOTS, type DaoEquipmentSlot } from '../engine/combat-v6/equipment/types'; -import { CHARACTER_MANUALS_V1 } from '../engine/combat-v6/manuals/content'; import { WILD_REGION } from '../engine/combat-v6/wild/content'; -import { MANUAL_JADES } from '../items/definitions/manual-jades'; import { findItemDefinition } from '../items/registry'; import raw from './data/wild.json'; @@ -22,7 +20,6 @@ export const WildRewardPackShape = z.strictObject({ source: z.discriminatedUnion('kind', [ z.strictObject({ kind: z.literal('fixed'), entries: z.array(z.strictObject({ rewardId: z.string().min(1), weight: z.number().positive().max(1000000), quantity })).min(1) }), z.strictObject({ kind: z.enum(['equipment', 'blueprints']), slots: z.array(z.enum(DAO_EQUIPMENT_SLOTS)).min(1) }), - z.strictObject({ kind: z.literal('manualJades') }), ]), })).min(1), }); @@ -33,7 +30,7 @@ function compilePool(pack: z.infer): DropPool { groups: pack.groups.map(group => { const source = group.source; const entries = source.kind === 'fixed' ? source.entries - : source.kind === 'manualJades' ? MANUAL_JADES.map(jade => entry(jade.id, CHARACTER_MANUALS_V1.find(m => m.id === jade.manualId)!.dropWeight)) + : pack.equipmentLevels.flatMap(level => source.slots.map(slot => entry(`${source.kind === 'blueprints' ? 'blueprint' : 'equipment'}.${slot}.${level}`))); return { id: group.id, chance: group.chance, entries }; }),