Files
90CNServer/pvf/docs/文件GUI模式.md
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

120 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 文件 GUI 模式
GUI 模式与侧边文件预览独立。文件默认显示文本,匹配 GUI provider 后才显示模式切换。当前支持 `.shp` 商店、全局掉率与 `.skl` 技能参数。
## 扩展入口
- `frontend/src/gui/registry.ts` 注册 provider:唯一 `id`、显示名称、`readOnly` 能力、文件匹配条件及异步 Vue 组件。
- provider 组件接收 `file`(文件索引、路径、当前草稿文本、可编辑标记)和 `active`。通过 `FileGUIHost` 装载;隐藏时保留组件状态,关闭文件时销毁。
- 显示模式归属窗格,每个窗格独立保存文件模式。文本编辑器使用隐藏而非卸载保留光标、滚动和撤销历史。
- `useFileGUIStore` 提供归档 epoch 与内容 revision。异步读取必须检查请求代次,防止关闭、切换归档或重复请求的旧结果覆盖新内容。
## 商店数据
`FileGUIService.ReadShop(fileIndex, text)` 只读取数据:商店结构来自调用者传入的当前草稿;商品、材料和职业引用来自当前归档 overlay。展示使用 overlay;编辑表单通过 `ReadItem(id, drafts)` 优先读取关联商品的未保存草稿。
`ShopDocument` 保留 Tab、分类区块及商品出现顺序,不合并重复商品。区块和商品记录包含 UTF-16 源位置,用于精确修改或删除某一次商品出现。
商品成本来自商品文件:`[price]` 为金币,`[need material]` 每两个值组成道具 ID 和数量。两类成本共存;缺失成本与显式零金币不同。未知引用和异常结构返回数据提示,缺图使用占位。
商店同时支持旧版 `[item list]` 整数列表及 115CN 的
`[sell item list]/[item]/[index]` 记录列表,兼容 `sell info/tab`、
根级 `tab` 及没有显式分页的 `sell info`;分类列表可位于显式或隐式分页内。
新记录的直接整数是记录键,`index` 是商品 ID;`index = -1` 的空位不展示且保留。
替换仅改商品 ID,删除整条记录,限购、购买数量等其他字段原样保留。
新增记录不复制其他商品的局部配置,记录键避开整份商店已有记录键。
115CN 商品记录内的 `price`、`need material` 分别优先于共享金币/材料成本。
局部成本只读,替换商品时保留;GUI 拒绝其单条成本修改及包含它们的批量成本修改。
此类商品仍可替换 ID 或删除。没有显式分页的文件可编辑商品,但分页管理需文本模式。
缺少分类 ID 的商品显示在“未分类”入口,并保留结构诊断;闭合异常区块不允许 GUI 编辑。
`[use category]` 的 `expert job` 与 `expert job non filter` 使用相同的副职业枚举:0 炼金术师、1 附魔师、2 控偶师、3 分解师。展示保留原始分类类型。
职业关系与搜索索引均使用角色文件 `[growtype name]` 的第一个值,缺失时回退 `[name]`。此规则位于共享元数据提取及关联名称解析中;语义缓存版本变化会使已有内存持久化缓存和 SQLite 缓存失效。
## 编辑与检索
- 商品 hover 或键盘聚焦后显示编辑、删除按钮。更换商品 ID 会先读取新商品的成本,再允许修改;删除只移除当前出现,不删除物品文件。
- 底部入口为添加商品、分页管理、批量设置。分页管理支持新增、重命名及删除,删除分页会同时移除其中全部分类的引用。
- 批量设置覆盖当前分页的全部大分类,按关联物品文件去重。金币和兑换材料可分别选择保持或替换;空金币会移除 `[price]`,空材料列表会移除 `[need material]`。
- 价格是物品文件的共享属性,修改会影响所有引用它的商店;界面明确提示此行为。
- `ApplyShopEdit` 校验归档 revision、文件路径与定位,再在独立 staging 中构造所有受影响文件;全部校验完成后一次性提交 overlay,并记录一次版本撤销操作。原 PVF 不会自动写盘。
- 前端携带未保存草稿,后端只使用实际受影响的文件;前端提交期间锁定文本输入,成功后同步相关标签。过期表单会被拒绝,不覆盖更新后的内容。
- `ItemPicker.vue` 是通用装备/道具选择器,支持图标、ID/名称/路径检索、延迟搜索、分页及过期请求隔离。`ArchiveService.SearchItems` 在内存和 SQLite 索引中都先限定物品范围再分页。
## 技能参数
- 工具菜单的「技能参数」仅扫描当前归档 `skill/` 目录及子目录中的 `.skl` 文件,支持按职业、名称、路径筛选。选择技能只加载独立草稿,不创建编辑器标签;点击「在文件 GUI 中打开」才将同一草稿转为标签。两个入口使用相同的 `SkillViewer.vue`,已有标签时共享内容。
- 通用 GUI 切换技能或关闭窗口会保留草稿,草稿参与单技能保存、主工具栏保存、自动保存和归档关闭保护,不占用编辑器标签上限。归档关闭时清空;归档重载、批处理和文件索引变化时按现有文件刷新规则同步。
- `FileGUIService.ReadSkill` 读取当前草稿,分别提取 `[dungeon]` 与 `[pvp]` 的静态数据、等级数据和描述绑定。等级表首个数值为列数,各模式按实际数据行数提供等级滑块。
- `[level property]` 前两个值忽略,模板先解析字符串引用,再统一处理字面换行及转义的 `\n`、`\r\n`、`\r`。按模板内容识别 `<int>` / `<quorum>` 整型与 `<floatN>` / `<decimalN>` 小数占位符,在后端标准化为同一预览格式,不依赖 PVF 变体编号;支持同一模板混用、多行、多占位符及 `%%` 转义。非负数据类型引用静态数据,负数据类型引用当前等级的动态数据,预览乘以绑定倍率。模式内部的描述优先于顶层共享描述。
- 描述数值、静态数据和表格单元格均可点击打开弹窗。动态参数默认修改全部等级,也可选择仅当前等级;支持直接赋值、加固定值、乘固定值。编辑原始数值,倍率仅用于预览;整数运算结果四舍五入。
- 编辑仅替换目标 token 的 UTF-16 源位置,保留其他字段、注释和换行;所有数值及位置校验通过后一次性更新草稿。沿用编辑器保存、脏状态和关闭保护,原 PVF 不会自动写盘。
- 两个技能 GUI 入口共用「保存技能」按钮,仅保存当前技能草稿到工作区;写入 PVF 使用主工具栏保存。没有修改或正在保存时禁用按钮,未应用的参数弹窗会阻止保存。
- 异常列数、重复数据区块和复杂表达式绑定显示提示;无法确定位置的数据禁止编辑,缺失绑定的占位符显示「无对应数据」。
- 真实样例为 `skill/atgunner/c4.skl`;使用 `PVF_TESTFILE=/path/to/90CN.pvf go test ./services -run '^TestSkill' -count=1` 验证技能扫描与参数修改编解码往返。
- 110US 使用 `skill/atgunner/c4.skl` 与 `skill/gunner/c4.skl` 验证引用模板、六项参数绑定、转义换行和参数编辑往返:`PVF_TESTFILE=/path/to/110US.pvf go test ./services -run '^TestSkillReal110US$' -count=1`。
## 加载状态与验证
首次加载使用两列骨架屏,刷新保留上次内容并暂时禁用分类和 Tab;失败显示重试入口,明确标记旧数据。图标独立加载和淡入,遵守系统减少动态效果设置。
验证命令:
```sh
go test ./...
PVF_TESTFILE=/path/to/90CN.pvf go test ./services -run 'TestShopReal90CN|TestShopEditReal90CNRoundTrip' -count=1
cd frontend
npm test
npm run build
```
真实样例为 `itemshop/101_joann_box.shp` 和 `itemshop/equipmentshop1.shp`。图标需要在应用中配置可用的 NPK 资源目录。
## 装备与道具基础编辑
- `.equ` 与 `.stk` 文件支持「物品」GUI,入口追加在现有文件 GUI provider 后。使用当前标签草稿,可编辑基础信息、职业限制、装备数值、多行说明,以及图标和地面图像引用;装备页复用游戏风格预览。
- 点击字段旁的「编辑」或「添加」打开表单。「应用修改」更新当前草稿,「保存物品」保存到工作区;写入 PVF 继续使用主工具栏保存。表单应用前校验归档 epoch、文件路径和草稿基线,拒绝覆盖更新后的内容。
- `frontend/src/gui/item.ts` 按原始 token 位置修改,保留未知段落、嵌套触发器、注释、换行和字符串类型标记。缺失字段追加到末尾;已有多值字段保留参数数量与顺序,不自动排序或归一化全文。
- 重复字段、复杂嵌套、特殊数值类型与字符串表引用显示只读提示,仍可通过 DSL 编辑。引用文本优先显示已有标注中的译文,不把译文替换成脚本字面字符串。数值使用脚本原始单位,基础表单不施加特定 90 版本的等级上限或属性换算。
- 本期未引入原编辑器的 Python/.NET 桥接、静态技能/特效数据库;礼包与新建登记见下一节,触发器与特效见后文。
- 针对性验证:`cd frontend && npm test -- tests/item-gui.test.ts tests/file-gui.test.ts`。
## 礼包配置与物品创建
- 普通礼包(`[booster]`)在基础字段后展示奖励列表,奖励同时展示关联物品名称与 ID,可增删奖励、通过通用物品选择器查找物品,以及修改数量和原始权重。名称按奖励 ID 批量查询并去重,复用归档的装备/道具关联及译文解析;修改奖励 ID、归档切换或内容刷新后重新读取,旧请求不会覆盖新结果,未知或未命名物品保留 ID。已有 `etc` / `stackable` 奖励段保留类型与定位,支持段首随机获得项数后连续排列多组「物品 ID、权重、数量」;GUI 按奖励池显示并可修改随机获得项数;新增候选物品追加到指定池,不创建额外抽取池。编辑和删除按组定位,保留剩余奖励的原文,删除整个段内奖励时移除该段;修改不会重建整个文件。
- 礼包内容支持直接行内编辑,保留原「编辑礼包」按钮。分类下拉框旁提供新增、重命名、删除操作,支持单层分类和二维父/子分类;删除分类同步调整名称、坐标及矩形占位,保留其余奖励块原文。无分类礼包添加分类会把已有物品保留在默认分类;删除最后分类后变为无分类空列表。每行提供编辑、删除按钮,可在当前列表中选择物品、修改数量、随机权重及 avatar 附加参数;随机奖励池可直接修改抽取项数和添加候选。删除前在内容区确认;修改写入当前文件草稿,支持撤销,保存 PVF 后落盘。尚未应用的行内表单参与草稿检查,外部草稿变化后不允许覆盖旧版本。
- 礼包内容以图标、物品名、数量的滚动列表展示,物品名及强化前缀复用商店 GUI 的稀有度颜色(含神话、太初渐变),缺少稀有度时使用相同的默认青色;自选礼包有分类时使用下拉框切换,二维分类显示两个联动下拉框,无分类时隐藏下拉框。自选奖励右侧提供预览复选框,最多选择配置的可选数量,切换分类清空选择;预览不会修改文件。普通礼包不显示复选框,保留权重显示。图标仅批量读取当前分类的奖励元数据,缺少资源时使用占位图。
- 自选礼包(`[booster selection]`)支持奖励增删、可选数量、提示与分类文本编辑,以及新增单层分类。`[booster category num] 0` 按无分类候选列表处理:一个 `0 0` 内容段、一条选择提示,奖励容器兼容 `etc`、`stackable`、`equipment` 与 `avatar`;`avatar` 按 ID、数量、两个附加参数四项一组解析,附加参数可编辑且保留原值,新增奖励沿用已有参数,编辑时保留原标签及零维度,不自动创建分类。无分类列表兼容完全省略 `[booster category name]`,编辑奖励或可选数量时保留缺省,不自动补写该段。`1 0` 同样按无分类列表显示,兼容单条空字符串提示及两条旧式名称;编辑时保留原维度和空提示,不自动补写名称。分类内支持 `[booster equipment upgrade]` 强化等级,物品名称显示为「+10 物品名」,仅作用于对应分类,编辑奖励时保留该字段。已有二维分类按坐标显示并可修改奖励,保留层级和坐标。兼容只含坐标的空分类及空奖励容器,允许末尾空分类省略名称;未修改时保留原文,编辑其他奖励不会补写空位。已有名称的空分类和新增分类可添加奖励并沿用原礼包的奖励容器类型;没有名称的空位需先通过 DSL 补充名称。分类文本为字符串表引用时不覆盖引用;包含特殊奖励结构、重复或不匹配坐标时显示只读提示。
- 物品 GUI 顶部「新建物品」支持复制当前草稿、选择归档中的参考物品,以及使用材料、普通礼包、自选礼包、武器、防具基础模板。参考物品使用当前未保存草稿;新名称写入复制文件,不修改原名称引用的字符串表。模板中的默认奖励 ID 仅为占位,创建后应配置实际奖励。
- 「检查 ID 与路径」同时查询装备和道具的 LST,包含未保存的列表草稿,区分重复 ID、已有文件与悬空路径登记。新 ID 使用 1–2147483647 的整数;路径限定为 `equipment/` 下的 `.equ` 或 `stackable/` 下的 `.stk`。
- `FileGUIService.CreateItem` 在独立暂存归档中完成新脚本、目标 LST 和需要的 indexhash 更新。90 系列表使用目录相对路径,集中 `list/` 布局使用根相对路径。提交前校验 revision,完成目录/SQLite 投影后一次发布,记录一次「新建并登记物品」版本操作。失败不会留下半个文件或单独登记。
- 未保存的其他列表草稿参与查重,但只消费实际修改的目标 LST/indexhash 草稿;源物品及其他草稿保持未保存状态。前端在提交期间锁定编辑,并仅推进被消费文件的基线。创建完成自动打开新文件的物品 GUI;此操作写入工作区,主工具栏保存才会写入 PVF。
- 验证:`go test ./services -run '^TestItemCreation' -count=1`;`cd frontend && npm test -- tests/item-booster.test.ts tests/item-creation-store.test.ts tests/item-gui.test.ts`。覆盖复制引用、两种列表布局、草稿查重、失败原子性、SQLite 投影与版本撤销;真实客户端写盘与游戏内行为仍需使用对应 PVF 做集成验证。
## 装备技能增强
- 装备 GUI 在已有字段后追加「技能增强」区,支持 `[skill levelup]`、`[skill data up]` 和 `[all skill item container]` 的行内新增、编辑、删除;保存沿用当前文件草稿与工作区流程。
- 技能等级按「职业、技能 ID、等级加成」三项分组,技能数据按「职业、技能 ID、作用范围、数据类型、数据序号、操作符、数值」七项分组。同类新增记录优先合并已有有效段落,支持重复段落;删除与修改按 token 定位,保留注释、CRLF、字符串类型标记及未知段落。触发器内部的同名段落不作为顶层加成修改。
- 等级区间加成支持职业、成长类型、技能组、等级上下限、加成等级、技能阶段和额外条件。已有区间记录逐字段修改,保留未修改的条件;包含特殊结构的记录保留原文并提示使用 DSL,其他有效增强仍可编辑。
- 职业选项复用当前归档注册的技能树,技能名称与 ID 从该职业的技能 LST 读取。技能 ID 按职业隔离;缓存随归档或引用刷新失效,延迟响应不会写回旧归档。列表不可用时仍可手动填写原始职业标签及技能 ID。
- 等级加成使用整数,数据修改支持整数和小数;数据序号的含义由目标技能自身的数据表决定。未应用表单参与 GUI 草稿检查;保存、切换显示模式、外部草稿变化均保留现有一致性保护。
- 验证:`cd frontend && npm test -- tests/item-skill-effects.test.ts tests/item-skill-catalog.test.ts && npm run build`。本期未迁入原编辑器的静态技能特化库与自动生成描述;触发器和 obj/ptl 引用编辑与预览见后文。
- 技能数据修改表单提供「选择技能数据」:复用技能参数 GUI,按当前归档职业登记列表选择技能,点击属性描述数值或静态/等级数据表数值后,回填职业、技能 ID、`[static]` / `[level]` 和原始数据索引。选择模式不修改或保存技能;显示倍率与预览等级不参与回填,装备作用范围、操作符和数值保持原值。
## 装备触发器、被动对象与粒子
- 装备 GUI 在技能增强之后追加「触发器与特效」。按 `[if]` 与相邻 `[then]` / `[multiple then]` 分组,支持新增、删除触发器,逐项增删改条件和动作,以及随机分支管理。同一分支可以配置多个动作;目标、触发概率、分支概率、持续时间均作为独立设置,保持原文中的先后次序。
- 条件覆盖命中、施放技能、被击、周期、冷却、动作状态、附加物、目标类型、HP/MP 阈值、人数、连击、装备品级等;动作覆盖属性、增伤、异常状态、附加物、消耗、恢复、召唤、台词和被动对象。操作键条件只复用文件中已有的 `[command]` 数据,不生成跨版本二进制常量。未知动作及无法确认参数形状的结构显示「DSL 编辑」,不会自动归一化或删除。
- 普通触发器添加分支时转换为 `[multiple then]`,保留原动作;原分支未声明分支概率时补 `100.0`,新增分支概率为 `0.0`。不自动平均或覆盖已有概率,需按实际需求编辑「分支概率」。最后一个分支不能单独删除,可删除整个触发器。
- `[passive object]` 按七列记录编辑:对象 ID、X/Y/Z、等级、跟随参数、粒子路径。支持同段追加多个对象、单独删除记录和预览。资源选择器从当前归档查询对象、粒子、附加物、怪物及 APC,支持分页、手填 ID/路径、异步过期隔离;未引入原工具的静态 90 版本数据库。
- `ArchiveService.SearchEffectResources` 在分页前限定资源范围。对象、附加物及怪物使用现有语义索引;粒子按全归档 `.ptl` 路径检索,APC 直接读取当前版本的 `aicharacter.lst`,适配普通列表及 `list/` 集中列表布局。
- `FileGUIService.ReadItemEffectPreview` 使用当前归档 overlay 及匹配路径的未保存标签草稿,解析对象的 `[basic motion]`、`[basic action]` → `[BASE ANI]`、`[string data]` 和 `[add particles]` 引用。装备第七参数支持归档路径、相对装备路径及 `equipment/character/particle/` 中的裸文件名;粒子内部引用相对粒子文件解析。
- 预览复用 ANI 解析器与 NPK 图片缓存,按帧播放基础动画,并按粒子的寿命、频率、实例上限、位置、速度、加速度、重力和透明度近似叠加;支持暂停、重播、倍速及逐层开关。最多 16 个动画层、每层 64 个粒子和 1200 个唯一贴图帧。无 `[object]` 的粒子和缺失资源明确提示。预览不执行 ACT 战斗/碰撞脚本,不模拟旋转和随机速度;IMG 直接引用仅显示首帧。
- 所有写操作只更新当前草稿,按 token 位置保留注释、未知段落、类型标记、重复记录及换行。触发器实际发生变更时,仅调整当前组语法行首的缩进:条件/动作相对容器增加一级,多分支中的 then 再增加一级;不修改多行字符串内容,也不额外插入动作前的空行。无变化应用保持原文。编辑弹窗参与保存与离开保护;归档、文件或草稿变化后拒绝使用旧定位。原 PVF 仍通过主工具栏保存。
- 验证:`go test ./...`;`cd frontend && npm test && npm run build`。新增 `item-triggers.test.ts`、`effect-playback.test.ts` 与 `services/item_effects_test.go`,覆盖精确编辑、多分支、七列对象、资源分页、两种列表布局、草稿与缺失资源。本地浏览器使用合成数据验证表单应用、随机分支转换和预览弹窗;未进行真实客户端内效果验证。