Compare commits
36 Commits
5583dca2b6
...
new2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bb275607a | ||
|
|
95bf2a4e17 | ||
|
|
3f2989ab55 | ||
|
|
577a62193c | ||
|
|
8f89dd11fa | ||
|
|
c10e24709d | ||
|
|
f92502f4f5 | ||
|
|
fa58f699d9 | ||
|
|
90e8a4a284 | ||
|
|
39de30b9b4 | ||
|
|
8707316032 | ||
|
|
fdcd81c083 | ||
|
|
cd9e7296a1 | ||
|
|
be6d941b90 | ||
|
|
51256a2813 | ||
|
|
0ea0434087 | ||
|
|
1634f6a528 | ||
|
|
c183ae464a | ||
|
|
d140645aab | ||
|
|
c610829661 | ||
|
|
580a5ad131 | ||
|
|
c282adcf3b | ||
|
|
4900911cfc | ||
|
|
45e3e8712b | ||
|
|
f4150bd33c | ||
|
|
707e25e73f | ||
|
|
f53c4293df | ||
|
|
f67f8a4aa0 | ||
|
|
16854ab1b8 | ||
|
|
287989b425 | ||
|
|
63a9f6cba5 | ||
|
|
93dc59a616 | ||
|
|
8c8810bb7e | ||
|
|
7640957325 | ||
|
|
2dc44b2393 | ||
|
|
f2a4926c49 |
@@ -56,7 +56,7 @@ description: "Guides users through filling the official software copyright (计
|
||||
- 软件环境(操作系统 / 平台)
|
||||
- 编程语言
|
||||
- 源程序量(行数)
|
||||
- 主要功能(200-300 字)
|
||||
- 主要功能(申请表字段要求 500 字以上,覆盖各功能模块;设计说明书概述章节可用 200-300 字摘要版)
|
||||
- 技术特点(150-250 字,可选)
|
||||
|
||||
**功能模块(ManualModule[])**
|
||||
@@ -72,6 +72,7 @@ description: "Guides users through filling the official software copyright (计
|
||||
3. **设计说明书 / 用户操作手册**:调用 `buildManual` 的章节模板,可选其一:
|
||||
- 设计说明书:引言 / 运行环境 / 功能模块设计 / 数据结构与接口设计 / 安全与异常处理 / 联系方式
|
||||
- 用户操作手册:引言 / 运行环境 / 安装与启动 / 功能操作说明 / 常见问题 / 联系方式
|
||||
4. **填表指引**(用户需要时):见下方"填表指引文档"章节
|
||||
|
||||
### 源程序专项
|
||||
|
||||
@@ -80,10 +81,10 @@ description: "Guides users through filling the official software copyright (计
|
||||
#### 提交要求(来自登记规范)
|
||||
|
||||
- 取前 30 页 + 后 30 页,不足 60 页时全部提交
|
||||
- 每页不少于 50 行有效代码
|
||||
- 每页不少于 50 行有效代码(默认每页 50 行;用户要求更高密度时可调,如 60 行/页,但不得低于 50)
|
||||
- 页眉:左侧标注 `软件全称 版本号`,右侧标注 `第 N 页 共 M 页`
|
||||
- 9.5pt 等宽字体,每行不超过 84 字符(A4 折行限制)
|
||||
- 文件名示例:`软件全称_V1.0_源程序.pdf`
|
||||
- 9.5pt 等宽字体,每行不超过 84 字符(A4 折行限制;按显示宽度计:ASCII 记 1 列,中文/全角记 2 列)
|
||||
- 文件名示例:`软件全称_V1.0_源程序.pdf`;用户需要 docx/PDF 成品时按下方「docx 版本生成」章节产出
|
||||
|
||||
#### 三种生成方式(按用户掌握情况选用)
|
||||
|
||||
@@ -92,11 +93,17 @@ description: "Guides users through filling the official software copyright (计
|
||||
让用户提供源代码文件路径或粘贴代码文本,Skill 直接执行 `paginateCode` 逻辑:
|
||||
|
||||
```text
|
||||
1. 把 \r\n 替换为 \n,\t 替换为 4 空格
|
||||
2. 按每页 50 行切分,得到 N 页
|
||||
3. 若 N > 60:保留前 30 页 + 后 30 页,丢弃中间
|
||||
4. 给每页加上统一页眉
|
||||
5. 用 A4 纸张打印 / 导出 PDF
|
||||
1. 与用户确认源代码根目录范围(只采集约定目录,如 assets/script)
|
||||
2. 排除非源码:.md/.csv 等文档、.d.ts 声明文件、着色器/材质、空文件、
|
||||
构建产物、第三方框架目录(如 extensions/ 下的开源插件框架)
|
||||
3. 清理文件头(前 40 行)的第三方署名与版权行:他人 ID/昵称、QQ 号、
|
||||
第三方框架作者的 @Author/@LastEditors/@Date/@LastEditTime、引擎 Copyright
|
||||
声明;生成后必须 grep 复验清理干净
|
||||
4. 入口文件(如 Main.ts)优先排序,其余按路径字母序;文件间加注释分隔行
|
||||
5. 把 \r\n 替换为 \n,\t 替换为 4 空格
|
||||
6. 按每页 N 行切分(N ≥ 50,默认 50,可按用户要求调整),得到总页数
|
||||
7. 若总页数 > 60:保留前 30 页 + 后 30 页,丢弃中间
|
||||
8. 给每页加上统一页眉;建议沉淀为可重跑脚本(代码变更后重新生成)
|
||||
```
|
||||
|
||||
输出格式示例:
|
||||
@@ -148,7 +155,7 @@ description: "Guides users through filling the official software copyright (计
|
||||
| 3 | 注释行占比 > 40% | info | 建议补充有效代码 |
|
||||
| 4 | 含第三方 License / Copyright 声明且与著作权人不一致 | error | 必须删除或修改 |
|
||||
| 5 | 实际行数与速查表"源程序量"相差 > 10% | warn | 同步修改申请表 |
|
||||
| 6 | 单行字符数 > 84 | warn | 折行换段 |
|
||||
| 6 | 单行显示宽度 > 84 列(中文记 2 列) | warn | 折行换段,续行缩进 4 空格;生成 docx 时必须主动折行,勿依赖 Word 自动换行 |
|
||||
| 7 | 含与著作权人无关的手机号 / 邮箱 | info | 脱敏后提交 |
|
||||
| 8 | 混入 package-lock.json / node_modules / .min.js 等构建产物 | warn | 移除后再上传 |
|
||||
|
||||
@@ -158,6 +165,24 @@ description: "Guides users through filling the official software copyright (计
|
||||
- ⚠️ **建议修改**:无 error,有 warn,列出具体项
|
||||
- ❌ **必须修改**:有 error,阻断提交
|
||||
|
||||
#### docx 版本生成(用户需要成品文件时)
|
||||
|
||||
用 Node + `docx` 库(v9)把 txt/md 排成规范 docx,关键实践:
|
||||
|
||||
- **页眉用 Word 域自动页码**:`PageNumber.CURRENT` / `PageNumber.TOTAL_PAGES`,
|
||||
配右对齐 Tab 位;勿在正文硬编码"共 M 页"——折行增多页数时域自动跟随
|
||||
- **精确分页**:每 N 行代码一页,下一页首段用 `pageBreakBefore: true`,
|
||||
保证"每页行数"与规范声明一致
|
||||
- **主动折行**:超 84 显示列的行在生成时折断(续行缩进 4 空格);
|
||||
依赖 Word 自动换行会导致每页物理行数失控、分页错位
|
||||
- **排版**:A4;边距上下 1.27cm、左右 1.5cm;代码 9.5pt Consolas
|
||||
(中文回退宋体);行距适度压缩(如 230/240)确保每页行数放得下
|
||||
- **依赖隔离**:在材料目录建独立 `package.json` 再 `npm install docx`;
|
||||
中文目录名会导致 `npm init -y` 失败(Invalid name),需手写合法 name;
|
||||
不隔离会污染用户项目根依赖(装错后必须还原 package.json 并删除误装的包)
|
||||
- **文件占用**:写入报 `EBUSY` 说明用户正在 Word 中打开该 docx,
|
||||
提示关闭后重试,勿反复盲试
|
||||
|
||||
## 设计说明书与用户操作手册(详细)
|
||||
|
||||
Skill 必须能根据"功能模块"列表生成完整的说明书文档,章节结构与项目代码 `buildManual` 一致:
|
||||
@@ -256,6 +281,32 @@ Skill 必须能根据"功能模块"列表生成完整的说明书文档,章节
|
||||
|
||||
输出"通过 / 建议修改 / 必须修改"三档结论,列出问题清单。
|
||||
|
||||
## 填表指引文档
|
||||
|
||||
用户要求生成"填表指引/调表指引"时,按在线申请表「软件开发信息」的 12 个系统字段
|
||||
逐项输出(字段名以中国版权保护中心系统实际为准),每项含
|
||||
「可直接粘贴的填写内容 + 填写要点」:
|
||||
|
||||
1. 开发的硬件环境(开发机配置,按实际开发电脑填写)
|
||||
2. 运行的硬件环境(目标设备,要求写低一些以扩大兼容范围)
|
||||
3. 开发该软件的操作系统
|
||||
4. 软件开发环境 / 开发工具(引擎编辑器、IDE、平台调试工具等,列主要项即可)
|
||||
5. 该软件的运行平台 / 操作系统
|
||||
6. 软件运行支撑环境 / 支持软件(如宿主 App、云服务)
|
||||
7. 编程语言(主语言;勿把 JSON/配置格式算作语言)
|
||||
8. 源程序量(与提交材料逻辑一致)
|
||||
9. 开发目的(如实填写,不夸大商业目标)
|
||||
10. 面向领域 / 行业(按系统下拉选项就近选择,游戏软件归文化娱乐类)
|
||||
11. 软件的主要功能(500 字以上,系统要求;覆盖各功能模块,游戏术语、书面语)
|
||||
12. 软件的技术特点(150-250 字)
|
||||
|
||||
**易错提示(必须写进指引)**:第 1/2 项(开发 vs 运行硬件)、第 3/5 项(开发 vs
|
||||
运行操作系统)是两对容易填反的字段,审查员会核对逻辑一致性。
|
||||
|
||||
指引还应包含:办理入口与实名认证、在线填报步骤、鉴别材料上传要求与 PDF 命名规范、
|
||||
提交前自检清单(checkbox)、常见退回原因与规避(已处理项标 ✅)、时间线参考、
|
||||
材料重新生成命令。
|
||||
|
||||
## 关键规则
|
||||
|
||||
- 所有对外材料一律使用「本软件」指代软件本身,不出现具体产品名
|
||||
|
||||
@@ -1781,7 +1781,7 @@
|
||||
"propertyPath": [
|
||||
"_active"
|
||||
],
|
||||
"value": true
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1800,7 +1800,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -30,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -1821,7 +1821,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 150,
|
||||
"height": 95.5
|
||||
"height": 35.5
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1838,7 +1838,7 @@
|
||||
"propertyPath": [
|
||||
"_active"
|
||||
],
|
||||
"value": true
|
||||
"value": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1869,7 +1869,7 @@
|
||||
"propertyPath": [
|
||||
"_top"
|
||||
],
|
||||
"value": -8.2625
|
||||
"value": 6.737500000000001
|
||||
},
|
||||
{
|
||||
"__type__": "cc.TargetInfo",
|
||||
@@ -1885,7 +1885,7 @@
|
||||
"propertyPath": [
|
||||
"_bottom"
|
||||
],
|
||||
"value": -7.237500000000001
|
||||
"value": 7.762499999999999
|
||||
},
|
||||
{
|
||||
"__type__": "CCPropertyOverrideInfo",
|
||||
@@ -1933,7 +1933,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 2.75,
|
||||
"y": 17.75,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -3659,7 +3659,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -30,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -3766,7 +3766,7 @@
|
||||
"__id__": 202
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_active": false,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 208
|
||||
@@ -4202,7 +4202,7 @@
|
||||
"__id__": 220
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_active": false,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 226
|
||||
@@ -4217,7 +4217,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 2.75,
|
||||
"y": 17.75,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -4635,7 +4635,7 @@
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 150,
|
||||
"height": 95.5
|
||||
"height": 35.5
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -4664,8 +4664,8 @@
|
||||
"_target": null,
|
||||
"_left": 0,
|
||||
"_right": 0,
|
||||
"_top": -8.2625,
|
||||
"_bottom": -7.237500000000001,
|
||||
"_top": 21.7375,
|
||||
"_bottom": 22.7625,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 0,
|
||||
"_isAbsLeft": true,
|
||||
|
||||
@@ -11,29 +11,32 @@
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.6,
|
||||
"_duration": 0.65,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
},
|
||||
{
|
||||
"__id__": 7
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.2,
|
||||
"frame": 0.3,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.2,
|
||||
"frame": 0.3,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
"__id__": 13
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
@@ -90,7 +93,8 @@
|
||||
0.4,
|
||||
0.45,
|
||||
0.5,
|
||||
0.55
|
||||
0.55,
|
||||
0.6
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
@@ -140,7 +144,98 @@
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@5d13a",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 8
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "slash-horizontal1"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0.15,
|
||||
0.2,
|
||||
0.25,
|
||||
0.3,
|
||||
0.35,
|
||||
0.4,
|
||||
0.45,
|
||||
0.5,
|
||||
0.55,
|
||||
0.6
|
||||
],
|
||||
"_values": [
|
||||
null,
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d5755",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d5755",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@34609",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@35b2b",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e377",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@cb59a",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@5adf0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6c6f6",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,29 +11,32 @@
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.4583333333333333,
|
||||
"_duration": 0.5833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
},
|
||||
{
|
||||
"__id__": 7
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"frame": 0.16666666666666666,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"frame": 0.16666666666666666,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
"__id__": 13
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
@@ -89,7 +92,8 @@
|
||||
0.2916666666666667,
|
||||
0.3333333333333333,
|
||||
0.375,
|
||||
0.4166666666666667
|
||||
0.4166666666666667,
|
||||
0.4583333333333333
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
@@ -135,7 +139,95 @@
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@21b99",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 8
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "slash-horizontal1"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.20833333333333334,
|
||||
0.25,
|
||||
0.2916666666666667,
|
||||
0.3333333333333333,
|
||||
0.375,
|
||||
0.4166666666666667,
|
||||
0.4583333333333333,
|
||||
0.5,
|
||||
0.5416666666666666
|
||||
],
|
||||
"_values": [
|
||||
null,
|
||||
null,
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d5755",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@34609",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@35b2b",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e377",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@cb59a",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@5adf0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6c6f6",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,29 +11,32 @@
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.4166666666666667,
|
||||
"_duration": 0.7083333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
},
|
||||
{
|
||||
"__id__": 7
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"frame": 0.16666666666666666,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"frame": 0.16666666666666666,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
"__id__": 13
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
@@ -88,7 +91,8 @@
|
||||
0.25,
|
||||
0.2916666666666667,
|
||||
0.3333333333333333,
|
||||
0.375
|
||||
0.375,
|
||||
0.4166666666666667
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
@@ -130,7 +134,115 @@
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@daa57",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 8
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "fire-slash-horizontal1"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.16666666666666666,
|
||||
0.20833333333333334,
|
||||
0.25,
|
||||
0.2916666666666667,
|
||||
0.3333333333333333,
|
||||
0.375,
|
||||
0.4166666666666667,
|
||||
0.4583333333333333,
|
||||
0.5,
|
||||
0.5416666666666666,
|
||||
0.5833333333333334,
|
||||
0.625,
|
||||
0.6666666666666666
|
||||
],
|
||||
"_values": [
|
||||
null,
|
||||
null,
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@38937",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@31859",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@33f97",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0ff1b",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@4953c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@cb80d",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d0b5c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@4ce52",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@90cfe",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@17d94",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@64baf",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -11,39 +11,32 @@
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.625,
|
||||
"_duration": 0.75,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
},
|
||||
{
|
||||
"__id__": 7
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.20833333333333334,
|
||||
"frame": 0.375,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.20833333333333334,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.4583333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.4583333333333333,
|
||||
"frame": 0.375,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
"__id__": 13
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
@@ -103,7 +96,8 @@
|
||||
0.4583333333333333,
|
||||
0.5,
|
||||
0.5416666666666666,
|
||||
0.5833333333333334
|
||||
0.5833333333333334,
|
||||
0.625
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
@@ -165,7 +159,115 @@
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1b470",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 8
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "electric-slash-horizontal1"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.20833333333333334,
|
||||
0.25,
|
||||
0.2916666666666667,
|
||||
0.3333333333333333,
|
||||
0.375,
|
||||
0.4166666666666667,
|
||||
0.4583333333333333,
|
||||
0.5,
|
||||
0.5416666666666666,
|
||||
0.5833333333333334,
|
||||
0.625,
|
||||
0.6666666666666666,
|
||||
0.7083333333333334
|
||||
],
|
||||
"_values": [
|
||||
null,
|
||||
null,
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@f6490",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6c874",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@5b5e5",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@5cc03",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@161b8",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@01fef",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@70d19",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@34499",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@9ec10",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@9e7f6",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@99406",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -89,30 +89,33 @@
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@5cbcb",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3981b",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1837a",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@341ea",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@494bc",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@494bc",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
null
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
921
assets/resources/game/skill/anm/atk/fb-8.anim
Normal file
921
assets/resources/game/skill/anm/atk/fb-8.anim
Normal file
@@ -0,0 +1,921 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "fb-8",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 20,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 1.4,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
},
|
||||
{
|
||||
"__id__": 7
|
||||
},
|
||||
{
|
||||
"__id__": 13
|
||||
},
|
||||
{
|
||||
"__id__": 24
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.6,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.6,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 1.2,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 1.2,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 35
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.05,
|
||||
0.1,
|
||||
0.15,
|
||||
0.2,
|
||||
0.25,
|
||||
0.3,
|
||||
0.35,
|
||||
0.4,
|
||||
0.45,
|
||||
0.5,
|
||||
0.55,
|
||||
0.6,
|
||||
0.65,
|
||||
0.7,
|
||||
0.75,
|
||||
0.8,
|
||||
0.85,
|
||||
0.9,
|
||||
0.95,
|
||||
1,
|
||||
1.05,
|
||||
1.1,
|
||||
1.15,
|
||||
1.2,
|
||||
1.25,
|
||||
1.3,
|
||||
1.35
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 8
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node-001"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.05,
|
||||
0.1,
|
||||
0.15,
|
||||
0.2,
|
||||
0.25,
|
||||
0.3,
|
||||
0.35,
|
||||
0.4,
|
||||
0.45,
|
||||
0.5,
|
||||
0.55,
|
||||
0.6,
|
||||
0.65,
|
||||
0.7,
|
||||
0.75,
|
||||
0.8,
|
||||
0.85,
|
||||
0.9,
|
||||
0.95,
|
||||
1,
|
||||
1.05,
|
||||
1.1,
|
||||
1.15,
|
||||
1.2,
|
||||
1.25,
|
||||
1.3,
|
||||
1.35
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@2ac32",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@a38c9",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@005f7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@d585f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@740a0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@6b34e",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.VectorTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 14
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channels": [
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_nComponents": 3
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 15
|
||||
},
|
||||
"position"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 17
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.65,
|
||||
1.35
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 19
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.3,
|
||||
0.65,
|
||||
1,
|
||||
1.35
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -60,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -60,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -50,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -60,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 21
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [],
|
||||
"_values": [],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 23
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [],
|
||||
"_values": [],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.VectorTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 25
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channels": [
|
||||
{
|
||||
"__id__": 27
|
||||
},
|
||||
{
|
||||
"__id__": 29
|
||||
},
|
||||
{
|
||||
"__id__": 31
|
||||
},
|
||||
{
|
||||
"__id__": 33
|
||||
}
|
||||
],
|
||||
"_nComponents": 3
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 26
|
||||
},
|
||||
"position"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node-001"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 28
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.65,
|
||||
1.35
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.3,
|
||||
0.65,
|
||||
1,
|
||||
1.35
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -60,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -50,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -60,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -70,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": -60,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 32
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.65,
|
||||
1.35
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 0,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 0,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 0,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"broken": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 34
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [],
|
||||
"_values": [],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/fb-8.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/fb-8.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "b0bec9a1-c3c7-412f-b6de-e27ec78dcc86",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "fb-8"
|
||||
}
|
||||
}
|
||||
@@ -76,23 +76,23 @@
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@2cfc2",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@03bc8",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@1ca40",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@acc42",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@66637",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@aaff4",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@cf65d",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@05059",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@1e597",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@4b51f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -76,23 +76,23 @@
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@94f10",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@03bc8",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@ab87c",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@acc42",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@7c2ba",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@aaff4",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@d6871",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@05059",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@4cd95",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@4b51f",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -20,28 +20,31 @@
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
"__id__": 24
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -211,6 +214,139 @@
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "slash-horizontal1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 11
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 48
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "0aybWnCBJDBb3WOXwo/YxC"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": null,
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "45iYWOPyBCNKkmXE+dmADF"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "bdI0RFo3VCNKhgFojxldzA",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
@@ -221,7 +357,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
"__id__": 15
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
@@ -249,7 +385,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
"__id__": 17
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
@@ -261,6 +397,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_target_mode": 0,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
@@ -278,7 +418,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
"__id__": 19
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
@@ -312,7 +452,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
"__id__": 21
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
@@ -346,7 +486,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
"__id__": 23
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
|
||||
@@ -20,28 +20,31 @@
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
"__id__": 24
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -211,6 +214,139 @@
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "slash-horizontal1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 11
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 48
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "82TGDP+iVBv4P1whT99OBW"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": null,
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "daC3XwzZFG4JHd5pqsR70r"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "e8yfxZXCJCU6wnX0/ttDjH",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
@@ -221,7 +357,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
"__id__": 15
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
@@ -249,7 +385,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
"__id__": 17
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
@@ -261,6 +397,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_target_mode": 0,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
@@ -278,7 +418,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
"__id__": 19
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
@@ -312,7 +452,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
"__id__": 21
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
@@ -346,7 +486,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
"__id__": 23
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
|
||||
@@ -20,28 +20,31 @@
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
"__id__": 24
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -173,7 +176,7 @@
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@13b06",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@7e6d7",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
@@ -211,6 +214,139 @@
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "fire-slash-horizontal1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 11
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 29.777,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 48
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "61GNVJPatIJoTFQpCuL1tc"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": null,
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "64tuHxFsxBMK3c6Rl+GQll"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "05Mo/yQRtK5YAX1chbWL76",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
@@ -221,7 +357,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
"__id__": 15
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
@@ -249,7 +385,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
"__id__": 17
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
@@ -261,6 +397,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_target_mode": 0,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
@@ -278,7 +418,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
"__id__": 19
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
@@ -312,7 +452,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
"__id__": 21
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
@@ -346,7 +486,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
"__id__": 23
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
|
||||
@@ -20,28 +20,31 @@
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
"__id__": 24
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -211,6 +214,142 @@
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "electric-slash-horizontal1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 11
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 25.352,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 48
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "598QTUIYhAD5NxyPBcSrlV"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@f6490",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "79xdyqEqhFdZmXNHgUvUaA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "d1hgL10YVE8a0CvDCyOdTd",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
@@ -221,7 +360,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
"__id__": 15
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
@@ -249,7 +388,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
"__id__": 17
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
@@ -261,6 +400,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_target_mode": 0,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
@@ -278,7 +421,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
"__id__": 19
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
@@ -312,7 +455,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
"__id__": 21
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
@@ -346,7 +489,7 @@
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
"__id__": 23
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
|
||||
@@ -35,10 +35,13 @@
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 16
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -92,8 +95,8 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 6.77,
|
||||
"y": 0,
|
||||
"x": 0,
|
||||
"y": -60,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -105,8 +108,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"x": 1.4,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -133,13 +136,13 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 90,
|
||||
"height": 90
|
||||
"width": 128,
|
||||
"height": 128
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
@@ -170,7 +173,7 @@
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@4d4df",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
@@ -222,13 +225,13 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 40
|
||||
"width": 20,
|
||||
"height": 100
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
@@ -248,13 +251,26 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"runType": 3,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"distance": 0,
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_target_mode": 0,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "60LInmZXxDtKu79AshRG9j"
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
@@ -288,7 +304,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "e1yBA625RLwLhzo6bLYW7j"
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
@@ -315,14 +331,43 @@
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 40
|
||||
"width": 20,
|
||||
"height": 100
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "43ZDJfXX9AX73gyytKQZWm"
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "45605d53-4dca-4529-819d-4a0d98c3a47c",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "45605d53-4dca-4529-819d-4a0d98c3a47c",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "fc32700f-bf4a-47ce-b619-6497b7280fde",
|
||||
"uuid": "7edb8215-1344-4215-8172-7a50a72da8bb",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
|
||||
@@ -35,10 +35,13 @@
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 16
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -92,7 +95,7 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 6.77,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
@@ -100,13 +103,13 @@
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
"z": 0.706489444943837,
|
||||
"w": 0.7077235789366842
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.3,
|
||||
"y": 0.3,
|
||||
"x": -1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -115,7 +118,7 @@
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
"z": 89.9
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
@@ -133,8 +136,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 90,
|
||||
"height": 90
|
||||
"width": 85,
|
||||
"height": 119
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -170,7 +173,7 @@
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@179ff",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@59ec8",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
@@ -222,8 +225,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 40
|
||||
"width": 50,
|
||||
"height": 50
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -248,13 +251,25 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"atk_x": 20,
|
||||
"atk_y": 20,
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"distance": 0,
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 100,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "60LInmZXxDtKu79AshRG9j"
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
@@ -288,7 +303,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "e1yBA625RLwLhzo6bLYW7j"
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
@@ -315,14 +330,43 @@
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 40
|
||||
"width": 50,
|
||||
"height": 150
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "43ZDJfXX9AX73gyytKQZWm"
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "45385cba-7334-4ff4-a548-578262417f58",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "45385cba-7334-4ff4-a548-578262417f58",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "5a0c6727-c741-4a33-b6f2-b2c56e3fc540",
|
||||
"uuid": "7c7ff7a4-17d5-4c4a-8fee-68b73af250e5",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
|
||||
519
assets/resources/game/skill/atk/m5.prefab
Normal file
519
assets/resources/game/skill/atk/m5.prefab
Normal file
@@ -0,0 +1,519 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "m5",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "m5",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 24
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -0.5,
|
||||
"y": 0.5,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 277,
|
||||
"height": 250
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@260b5",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node-001",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 11
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -0.5,
|
||||
"y": 0.5,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 122,
|
||||
"height": 68
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "4blcTDlixIRI3qtyERLs2E"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": null,
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": null,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "e6QtagQwZA/50lsdWvOjb0"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "ed54OCubBD853t6+5pUPLQ",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 50,
|
||||
"height": 100
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"distance": 0,
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 19
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 21
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 50
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 50,
|
||||
"height": 100
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 23
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "a71379d4-b14d-4070-96ee-e7c889f25bbd",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "a71379d4-b14d-4070-96ee-e7c889f25bbd",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -2,12 +2,12 @@
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "91e5e3e3-d2f1-4522-9f82-649e9bc8f8db",
|
||||
"uuid": "8dc65122-222f-4bd3-b89c-62e7f8eefe78",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "up_red"
|
||||
"syncNodeName": "m5"
|
||||
}
|
||||
}
|
||||
@@ -293,6 +293,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 4,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 100,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
@@ -293,6 +293,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 6,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 100,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
@@ -261,6 +261,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 3,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 100,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
@@ -397,6 +397,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 3,
|
||||
"summon_target_mode": 0,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0.2,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -30,
|
||||
"y": -90,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -108,8 +108,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0.9,
|
||||
"y": 1,
|
||||
"x": 1.5,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -136,8 +136,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 65,
|
||||
"height": 330
|
||||
"width": 128,
|
||||
"height": 128
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -173,7 +173,7 @@
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@5cbcb",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
@@ -261,6 +261,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 5,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
@@ -261,6 +261,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 3,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0.5,
|
||||
"height_offset": 100,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
@@ -261,6 +261,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 3,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 100,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
@@ -261,6 +261,10 @@
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 3,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0,
|
||||
"height_offset": 100,
|
||||
"_id": ""
|
||||
},
|
||||
|
||||
526
assets/resources/game/skill/atk/maxm7.prefab
Normal file
526
assets/resources/game/skill/atk/maxm7.prefab
Normal file
@@ -0,0 +1,526 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "maxm7",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "maxm7",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
},
|
||||
{
|
||||
"__id__": 18
|
||||
},
|
||||
{
|
||||
"__id__": 20
|
||||
},
|
||||
{
|
||||
"__id__": 22
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 24
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -30,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 128,
|
||||
"height": 128
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node-001",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 9
|
||||
},
|
||||
{
|
||||
"__id__": 11
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -30,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 2,
|
||||
"y": 2,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 10
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 128,
|
||||
"height": 128
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "cbS/mBx7hFKK1RlGcpOgD1"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 8
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@55402",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "eah7cf5S5FCrmP7CHX5e38"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "f9cYdo2HdAp7KWposjmIcg",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 200,
|
||||
"height": 100
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.3
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"runType": 3,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"distance": 0,
|
||||
"forceStartX": -1,
|
||||
"forceStartY": -1,
|
||||
"summon_count": 1,
|
||||
"summon_target_mode": 2,
|
||||
"summon_x_offset": 0,
|
||||
"summon_y_offset": 0,
|
||||
"summon_interval": 0.5,
|
||||
"height_offset": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 19
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 21
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 200,
|
||||
"height": 100
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 23
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "b0bec9a1-c3c7-412f-b6de-e27ec78dcc86",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "b0bec9a1-c3c7-412f-b6de-e27ec78dcc86",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -2,12 +2,12 @@
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "55e98478-734f-4d85-92b2-0a62a52c494c",
|
||||
"uuid": "2316d214-bb61-434e-a056-48793e72dcce",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "up_blue"
|
||||
"syncNodeName": "maxm7"
|
||||
}
|
||||
}
|
||||
@@ -158,13 +158,13 @@
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"r": 10,
|
||||
"g": 206,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@2cfc2",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@03bc8",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
|
||||
@@ -159,12 +159,12 @@
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"g": 12,
|
||||
"b": 12,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@94f10",
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@03bc8",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "up_blue",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "up_blue",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "skill",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 77,
|
||||
"height": 77
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b4sNQPJWFKha7x75SWXmRj"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 206
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@e7e9c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 1,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0.2,
|
||||
"_fillRange": 1,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9a+65VIghBm4HQxuHPQ/mg"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "7a5LvbdlxEc6FKa7cun2oB",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 110
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "81e87781-7328-4283-a747-060707c41880",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "81e87781-7328-4283-a747-060707c41880",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c6LOemuvJKyYCqlF/yUJcr"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -1,274 +0,0 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "up_green",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "up_green",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "skill",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 77,
|
||||
"height": 77
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b4sNQPJWFKha7x75SWXmRj"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 206
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@e7e9c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 1,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0.2,
|
||||
"_fillRange": 1,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9a+65VIghBm4HQxuHPQ/mg"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "7a5LvbdlxEc6FKa7cun2oB",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 110
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "01337a12-a04d-41f5-8f1f-efbf16989fb3",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "01337a12-a04d-41f5-8f1f-efbf16989fb3",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c6LOemuvJKyYCqlF/yUJcr"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -1,274 +0,0 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "up_red",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "up_red",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "skill",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 77,
|
||||
"height": 77
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b4sNQPJWFKha7x75SWXmRj"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 206
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@e7e9c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 1,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0.2,
|
||||
"_fillRange": 1,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9a+65VIghBm4HQxuHPQ/mg"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "7a5LvbdlxEc6FKa7cun2oB",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 110
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "0f9dab25-82b0-4b96-b649-7c6162deafa0",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "0f9dab25-82b0-4b96-b649-7c6162deafa0",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c6LOemuvJKyYCqlF/yUJcr"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -1,274 +0,0 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "up_blue",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "up_blue",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "skill",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -7.953,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 77,
|
||||
"height": 77
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b4sNQPJWFKha7x75SWXmRj"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 206
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@e7e9c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 1,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0.2,
|
||||
"_fillRange": 1,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9a+65VIghBm4HQxuHPQ/mg"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "7a5LvbdlxEc6FKa7cun2oB",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 110
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "81e87781-7328-4283-a747-060707c41880",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "81e87781-7328-4283-a747-060707c41880",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c6LOemuvJKyYCqlF/yUJcr"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
{"ver":"1.1.50","importer":"prefab","imported":true,"uuid":"1b44e122-28a1-4b56-964c-718f6f68737f","files":[".json"],"subMetas":{},"userData":{"syncNodeName":"up_blue"}}
|
||||
@@ -1,274 +0,0 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "up_green",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "up_green",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "skill",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -5.121,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 77,
|
||||
"height": 77
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b4sNQPJWFKha7x75SWXmRj"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 206
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@e7e9c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 1,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0.2,
|
||||
"_fillRange": 1,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9a+65VIghBm4HQxuHPQ/mg"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "7a5LvbdlxEc6FKa7cun2oB",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 110
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "01337a12-a04d-41f5-8f1f-efbf16989fb3",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "01337a12-a04d-41f5-8f1f-efbf16989fb3",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c6LOemuvJKyYCqlF/yUJcr"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
{"ver":"1.1.50","importer":"prefab","imported":true,"uuid":"dd573541-6f27-4dc4-9e86-c98caf79a58d","files":[".json"],"subMetas":{},"userData":{"syncNodeName":"up_green"}}
|
||||
@@ -1,274 +0,0 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "up_red",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "up_red",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 12
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "skill",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -7.795,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.3,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 77,
|
||||
"height": 77
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b4sNQPJWFKha7x75SWXmRj"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 206
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6@e7e9c",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 1,
|
||||
"_fillType": 1,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0.2,
|
||||
"_fillRange": 1,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "9a+65VIghBm4HQxuHPQ/mg"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "7a5LvbdlxEc6FKa7cun2oB",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 80,
|
||||
"height": 110
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "0f9dab25-82b0-4b96-b649-7c6162deafa0",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "0f9dab25-82b0-4b96-b649-7c6162deafa0",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "c6LOemuvJKyYCqlF/yUJcr"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
{"ver":"1.1.50","importer":"prefab","imported":true,"uuid":"34fd7720-511b-4935-a1c6-d23e9e095faa","files":[".json"],"subMetas":{},"userData":{"syncNodeName":"up_red"}}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 190 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 948 KiB |
File diff suppressed because it is too large
Load Diff
5172
assets/resources/gui/element/TIBox.prefab
Normal file
5172
assets/resources/gui/element/TIBox.prefab
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,12 +2,12 @@
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "deb4a1bb-3f47-4c7c-9be1-352518675a16",
|
||||
"uuid": "e2394eb1-27f0-4b7c-ace3-cfb3453eef99",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "up_green"
|
||||
"syncNodeName": "TIBox"
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 75,
|
||||
"y": 80,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -338,8 +338,8 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 160.26666666666668,
|
||||
"height": 200
|
||||
"width": 200.26666666666665,
|
||||
"height": 213.33333333333334
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -358,8 +358,8 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 160.26666666666668,
|
||||
"height": 200
|
||||
"width": 200.26666666666665,
|
||||
"height": 213.33333333333334
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -379,8 +379,8 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 160.26666666666668,
|
||||
"height": 200
|
||||
"width": 200.26666666666665,
|
||||
"height": 213.33333333333334
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -400,7 +400,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 152.26666666666668,
|
||||
"width": 192.26666666666665,
|
||||
"height": 150
|
||||
}
|
||||
},
|
||||
@@ -422,7 +422,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21,
|
||||
"y": 27.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -443,7 +443,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 152.26666666666668,
|
||||
"width": 192.26666666666665,
|
||||
"height": 24
|
||||
}
|
||||
},
|
||||
@@ -465,7 +465,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 84,
|
||||
"y": 90.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -486,7 +486,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 140.26666666666668,
|
||||
"width": 180.26666666666665,
|
||||
"height": 150
|
||||
}
|
||||
},
|
||||
@@ -508,7 +508,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21,
|
||||
"y": 27.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -529,8 +529,8 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 160.26666666666668,
|
||||
"height": 200
|
||||
"width": 200.26666666666665,
|
||||
"height": 213.33333333333334
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -550,8 +550,8 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 160.26666666666668,
|
||||
"height": 200
|
||||
"width": 200.26666666666665,
|
||||
"height": 213.33333333333334
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -571,7 +571,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 152.26666666666668,
|
||||
"width": 192.26666666666665,
|
||||
"height": 150
|
||||
}
|
||||
},
|
||||
@@ -593,7 +593,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21,
|
||||
"y": 27.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -614,7 +614,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 152.26666666666668,
|
||||
"width": 192.26666666666665,
|
||||
"height": 24
|
||||
}
|
||||
},
|
||||
@@ -636,7 +636,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 84,
|
||||
"y": 90.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -657,7 +657,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 140.26666666666668,
|
||||
"width": 180.26666666666665,
|
||||
"height": 150
|
||||
}
|
||||
},
|
||||
@@ -679,7 +679,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21,
|
||||
"y": 27.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -752,8 +752,8 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 160.26666666666668,
|
||||
"height": 200
|
||||
"width": 200.26666666666665,
|
||||
"height": 213.33333333333334
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -773,8 +773,8 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 160.26666666666668,
|
||||
"height": 200
|
||||
"width": 200.26666666666665,
|
||||
"height": 213.33333333333334
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -794,7 +794,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 152.26666666666668,
|
||||
"width": 192.26666666666665,
|
||||
"height": 150
|
||||
}
|
||||
},
|
||||
@@ -816,7 +816,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21,
|
||||
"y": 27.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -837,7 +837,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 152.26666666666668,
|
||||
"width": 192.26666666666665,
|
||||
"height": 24
|
||||
}
|
||||
},
|
||||
@@ -859,7 +859,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 84,
|
||||
"y": 90.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -880,7 +880,7 @@
|
||||
],
|
||||
"value": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 140.26666666666668,
|
||||
"width": 180.26666666666665,
|
||||
"height": 150
|
||||
}
|
||||
},
|
||||
@@ -902,7 +902,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21,
|
||||
"y": 27.66666666666667,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
@@ -937,7 +937,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 50.892,
|
||||
"y": 59.957,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -1083,7 +1083,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 89.47899999999998,
|
||||
"y": 96.661,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -1244,6 +1244,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "08p7XmUnpCJKHgFSs+XBKi",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -1260,8 +1262,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 90,
|
||||
"height": 79.99999999999997
|
||||
"width": 120,
|
||||
"height": 90
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1312,8 +1314,8 @@
|
||||
"_target": null,
|
||||
"_left": 15,
|
||||
"_right": 15,
|
||||
"_top": 20.521,
|
||||
"_bottom": 49.479000000000006,
|
||||
"_top": 18.339,
|
||||
"_bottom": 51.66100000000001,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 0,
|
||||
"_isAbsLeft": true,
|
||||
@@ -1418,7 +1420,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 131.674,
|
||||
"y": 141.674,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -3037,8 +3039,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 150
|
||||
"width": 150,
|
||||
"height": 160
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
|
||||
15693
assets/resources/gui/element/heroInfo.prefab
Normal file
15693
assets/resources/gui/element/heroInfo.prefab
Normal file
File diff suppressed because it is too large
Load Diff
13
assets/resources/gui/element/heroInfo.prefab.meta
Normal file
13
assets/resources/gui/element/heroInfo.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "620ee559-61bc-470d-b85d-e881bac42067",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "heroInfo"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 962 KiB After Width: | Height: | Size: 1.0 MiB |
@@ -116,8 +116,8 @@
|
||||
"height": 54,
|
||||
"rawWidth": 89,
|
||||
"rawHeight": 54,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderTop": 26,
|
||||
"borderBottom": 26,
|
||||
"borderLeft": 44,
|
||||
"borderRight": 44,
|
||||
"packable": true,
|
||||
@@ -576,8 +576,8 @@
|
||||
"height": 45,
|
||||
"rawWidth": 52,
|
||||
"rawHeight": 45,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderTop": 22,
|
||||
"borderBottom": 23,
|
||||
"borderLeft": 26,
|
||||
"borderRight": 26,
|
||||
"packable": true,
|
||||
@@ -4992,8 +4992,8 @@
|
||||
"height": 45,
|
||||
"rawWidth": 81,
|
||||
"rawHeight": 45,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderTop": 22,
|
||||
"borderBottom": 22,
|
||||
"borderLeft": 40,
|
||||
"borderRight": 40,
|
||||
"packable": true,
|
||||
@@ -6096,10 +6096,10 @@
|
||||
"height": 39,
|
||||
"rawWidth": 37,
|
||||
"rawHeight": 39,
|
||||
"borderTop": 0,
|
||||
"borderBottom": 0,
|
||||
"borderLeft": 0,
|
||||
"borderRight": 0,
|
||||
"borderTop": 19,
|
||||
"borderBottom": 18,
|
||||
"borderLeft": 19,
|
||||
"borderRight": 18,
|
||||
"packable": true,
|
||||
"pixelsToUnit": 100,
|
||||
"pivotX": 0.5,
|
||||
|
||||
@@ -81,11 +81,13 @@ const PLACEHOLDER_PATTERN = /\{(\w+)\}/g;
|
||||
* atking/atked 为周期循环触发(运行时按次数取模判定),拼为"每攻击N次"/"每受击N次"
|
||||
* 表达循环语义(N=1 时退化为"每次攻击"/"每次受击");其余类型直接用触发名(召唤/死亡后/回合开始)。
|
||||
*/
|
||||
function buildTriggerLabel(type: SkillTriggerType, tNum: number): string {
|
||||
function buildTriggerLabel(type: SkillTriggerType, tNum: number, chance?: number): string {
|
||||
if (type === SkillTriggerType.Atking || type === SkillTriggerType.Atked) {
|
||||
// 配置了 t_chance 且 < 100 时追加概率说明(保底叠加为隐藏机制,不在文案展示)
|
||||
const chanceSeg = chance !== undefined && chance < 100 ? `(${valueRich(Math.round(chance))}%概率)` : "";
|
||||
return tNum <= 1
|
||||
? `每次${SkillTriggerName[type]}`
|
||||
: `每${SkillTriggerName[type]}${valueRich(tNum)}次`;
|
||||
? `每次${SkillTriggerName[type]}${chanceSeg}`
|
||||
: `每${SkillTriggerName[type]}${valueRich(tNum)}次${chanceSeg}`;
|
||||
}
|
||||
return SkillTriggerName[type];
|
||||
}
|
||||
@@ -137,18 +139,25 @@ function getBuffValue(skill: SkillConfig): number {
|
||||
* Why: 数值不写死在模板里,overrides 调整档位数值时文案自动同步。
|
||||
* @param tmpl 描述模板(含占位符)
|
||||
* @param skill mergeSkillParams 合并后的技能配置
|
||||
* @param extra 条目级参数占位符(触发技能的 t_chance/pity_add 不在 SkillConfig,由调用方透传;优先于技能字段)
|
||||
*/
|
||||
function renderDesc(tmpl: string, skill: SkillConfig): string {
|
||||
function renderDesc(tmpl: string, skill: SkillConfig, extra?: Record<string, number>): string {
|
||||
return tmpl.replace(PLACEHOLDER_PATTERN, (raw, key: string) => {
|
||||
if (key === "target") return buildTargetDesc(skill);
|
||||
if (key === "dur") return valueRich(getBuffDuration(skill));
|
||||
if (key === "buff_value") return valueRich(getBuffValue(skill));
|
||||
if (extra && key in extra) return valueRich(String(extra[key]));
|
||||
const v = (skill as unknown as Record<string, unknown>)[key];
|
||||
// 未定义数值字段按 0 渲染,避免面板出现 undefined(模板与配置不匹配的兜底)
|
||||
return v === undefined ? valueRich(0) : valueRich(String(v));
|
||||
});
|
||||
}
|
||||
|
||||
/** 触发档位的条目级占位符上下文(普攻/大招档位无此层参数,返回 undefined 走技能字段) */
|
||||
function tierExtras(t: ISkillTierInfo): Record<string, number> | undefined {
|
||||
return t.tChance === undefined ? undefined : { t_chance: t.tChance, pity_add: t.pityAdd ?? 0 };
|
||||
}
|
||||
|
||||
/** 无模板时兜底:回退底座技能的 info 备注 */
|
||||
function fallbackDesc(skill: SkillConfig): string {
|
||||
return String(skill.info ?? "");
|
||||
@@ -224,9 +233,12 @@ export function buildHeroSkillDesc(heroUuid: number, showName: boolean = true, s
|
||||
if (e.lv <= lv && e.desc) tmpl = e.desc;
|
||||
}
|
||||
tmpl = tmpl ?? entries[0]?.desc;
|
||||
const text = tmpl ? renderDesc(tmpl, skill) : fallbackDesc(skill);
|
||||
const text = tmpl
|
||||
? renderDesc(tmpl, skill, { t_chance: item.t_chance ?? 100, pity_add: item.pity_add ?? 0 })
|
||||
: fallbackDesc(skill);
|
||||
// 触发语境:"每攻击N次"循环语义(buildTriggerLabel),逗号衔接效果文本
|
||||
const trig = buildTriggerLabel(type, item.t_num);
|
||||
// 模板已显式使用 {t_chance} 时不再自动追加概率后缀,避免同一行重复展示
|
||||
const trig = buildTriggerLabel(type, item.t_num, tmpl?.includes("{t_chance}") ? undefined : item.t_chance);
|
||||
parts.push(`${trig}, ${text}`);
|
||||
}
|
||||
// dead 组内的复活效果(revive_hp / revive_count)并入同一行
|
||||
@@ -279,12 +291,17 @@ interface ISkillTierInfo {
|
||||
ownDesc?: string;
|
||||
/** 触发技能档位的触发次数(普攻无) */
|
||||
tNum: number;
|
||||
/** 触发技能档位的触发概率百分点(普攻无;缺省 100 必触发) */
|
||||
tChance?: number;
|
||||
/** 触发技能档位的保底叠加百分点(普攻无;缺省 0) */
|
||||
pityAdd?: number;
|
||||
}
|
||||
|
||||
/** 相邻档 diff 参与展示的字段(排除动画/弹道/特效等玩家无感字段),t_num 为触发技能专有 */
|
||||
const DIFF_KEYS: string[] = [
|
||||
"ap", "hit_count", "num", "summon_count", "crt", "frz", "stun", "para",
|
||||
"bck", "bck_chance", "buff_duration", "buff_value", "t_num", "timed_buff_id",
|
||||
"t_chance", "pity_add",
|
||||
];
|
||||
|
||||
/** diff 字段的称谓与单位("新增/漂移"描述共用) */
|
||||
@@ -302,11 +319,15 @@ const DIFF_META: Record<string, { label: string; unit: string }> = {
|
||||
buff_duration: { label: "持续", unit: "秒" },
|
||||
buff_value: { label: "增益强度", unit: "" },
|
||||
t_num: { label: "触发次数", unit: "次" },
|
||||
t_chance: { label: "触发概率", unit: "%" },
|
||||
pity_add: { label: "保底叠加", unit: "%" },
|
||||
};
|
||||
|
||||
/** 读取档位某展示字段的实时值(t_num/buff_value 走专用取值) */
|
||||
/** 读取档位某展示字段的实时值(t_num/t_chance/pity_add/buff_value 走专用取值) */
|
||||
function getTierValue(tier: ISkillTierInfo, key: string): number {
|
||||
if (key === "t_num") return tier.tNum;
|
||||
if (key === "t_chance") return tier.tChance ?? 100;
|
||||
if (key === "pity_add") return tier.pityAdd ?? 0;
|
||||
if (key === "buff_value") return getBuffValue(tier.skill);
|
||||
const v = (tier.skill as unknown as Record<string, unknown>)[key];
|
||||
return typeof v === "number" ? v : 0;
|
||||
@@ -497,9 +518,13 @@ function collectTriggerTiers(hero: heroInfo, type: SkillTriggerType, skillUuid:
|
||||
desc: e.desc ?? inherit,
|
||||
ownDesc: e.desc,
|
||||
tNum: e.t_num,
|
||||
tChance: e.t_chance ?? 100,
|
||||
pityAdd: e.pity_add ?? 0,
|
||||
};
|
||||
});
|
||||
const label = buildTriggerLabel(type, sorted[0].t_num);
|
||||
// slv1 模板已显式使用 {t_chance} 时不再自动追加概率后缀,避免同一行重复展示
|
||||
const firstTmpl = tiers[0]?.desc;
|
||||
const label = buildTriggerLabel(type, sorted[0].t_num, firstTmpl?.includes("{t_chance}") ? undefined : sorted[0].t_chance);
|
||||
return { tiers, label };
|
||||
}
|
||||
|
||||
@@ -510,11 +535,11 @@ function buildTierLines(tiers: ISkillTierInfo[], triggerLabel: string): HeroSkil
|
||||
const t = tiers[i];
|
||||
if (i === 0) {
|
||||
// 技能 1 级:完整能力 + 数值
|
||||
const text = t.desc ? renderDesc(t.desc, t.skill) : fallbackDesc(t.skill);
|
||||
const text = t.desc ? renderDesc(t.desc, t.skill, tierExtras(t)) : fallbackDesc(t.skill);
|
||||
lines.push({ sLv: t.sLv, heroLv: t.heroLv, text: triggerLabel ? `${triggerLabel}, ${text}` : text });
|
||||
} else if (t.ownDesc) {
|
||||
// 提升档:该档显式配置了模板(如护盾/治疗等 diff 标签语义不达的技能),策划表述优先
|
||||
lines.push({ sLv: t.sLv, heroLv: t.heroLv, text: renderDesc(t.ownDesc, t.skill) });
|
||||
lines.push({ sLv: t.sLv, heroLv: t.heroLv, text: renderDesc(t.ownDesc, t.skill, tierExtras(t)) });
|
||||
} else {
|
||||
// 提升档:未配置模板,给相对上一级的提升
|
||||
const diff = buildTierDiff(tiers[i - 1], t);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { GameScoreStats } from "./config/HeroAttrs";
|
||||
import { mLogger } from "./Logger";
|
||||
import { gameDataSync } from "./GameDataSync";
|
||||
import { HERO_UPGRADE_NEED_FRAGMENTS } from "./config/GameSet";
|
||||
import { TalentSet, TALENT_LEVEL_MAX, getTalentLevelExp, getTalentUpgradeCost, isTalentTierUnlocked } from "./config/TalentSet";
|
||||
import { TalentSet, TALENT_LEVEL_MAX, getTalentLevelExp, isTalentPrereqMet, isTalentTierUnlocked } from "./config/TalentSet";
|
||||
import { ScoreRankList, ScoreTier, getRankByScore } from "./config/ScoreRankSet";
|
||||
import { RANK_REWARDS, STAGE_REWARDS, RewardItem, pickRandomHeroUuid } from "./config/RewardSet";
|
||||
|
||||
@@ -48,11 +48,9 @@ export interface GameDate {
|
||||
talent_lv?: number,
|
||||
/** 天赋当前等级已累计的经验值 */
|
||||
talent_exp?: number,
|
||||
/** 关卡进度存档:当前章节-关-节 */
|
||||
/** 关卡进度存档:当前关卡号(无限关模式只记关卡号,回合恒从 1 开始) */
|
||||
stage_progress?: {
|
||||
chapter: number,
|
||||
stage: number,
|
||||
section: number,
|
||||
},
|
||||
/** 奖励领取状态:段位/闯关首达奖励的已领取标记(玩家点击领取) */
|
||||
reward_claimed?: {
|
||||
@@ -61,7 +59,7 @@ export interface GameDate {
|
||||
/** 已领取的大关号 */
|
||||
stage: number[],
|
||||
},
|
||||
/** 已通关大关的显式标记(覆盖存档停留场景,如第 10 关通关后进度停留 1-10-10) */
|
||||
/** 已通关大关的显式标记(历史遗留字段,无限关模式不再写入;仅用于兼容读取旧存档) */
|
||||
passed_stages?: number[],
|
||||
}
|
||||
export interface CloudData {
|
||||
@@ -152,11 +150,9 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
talent_lv: 0,
|
||||
/** 当前等级已累计经验 */
|
||||
talent_exp: 0,
|
||||
/** 关卡进度存档:默认从 1-1-1 开始 */
|
||||
/** 关卡进度存档:默认从第 1 关开始 */
|
||||
stage_progress: {
|
||||
chapter: 1,
|
||||
stage: 1,
|
||||
section: 1,
|
||||
},
|
||||
/** 奖励领取状态(玩家点击领取,跨局保留) */
|
||||
reward_claimed: {
|
||||
@@ -238,9 +234,7 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
switch_music: true, // 音乐开关(MVVM绑定字段)
|
||||
/** 关卡进度(MVVM绑定字段,镜像 data.stage_progress,供 HUD 实时显示) */
|
||||
stage_progress: {
|
||||
chapter: 1,
|
||||
stage: 1,
|
||||
section: 1,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -352,26 +346,26 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
* 将 data 层的关卡进度镜像到 vmdata 视图层(MVVM 绑定字段)。
|
||||
* Why: data.stage_progress 为云端持久权威源,vmdata.stage_progress 仅供 HUD 绑定展示;
|
||||
* 登录覆盖、关卡推进后都必须调用本方法保持镜像一致。
|
||||
* 旧版三段存档(chapter/stage/section)在同步时归一化为 stage 单字段。
|
||||
*/
|
||||
public syncStageProgressToView(): void {
|
||||
const p = this.data.stage_progress;
|
||||
// 就地更新字段而非整体替换对象:oops VM(JsonOb)只通知被赋值属性的精确路径,
|
||||
// 整体替换仅派发 VC:data.stage_progress,不会触发 HUD 绑定的
|
||||
// data.stage_progress.section 等嵌套末端路径刷新,导致进度显示停留在老节
|
||||
const view = this.vmdata.stage_progress;
|
||||
view.chapter = p?.chapter ?? 1;
|
||||
view.stage = p?.stage ?? 1;
|
||||
view.section = p?.section ?? 1;
|
||||
// 旧存档归一化:兼容历史 chapter/stage/section 三段结构,仅提取 stage
|
||||
const stage = Math.max(1, Math.floor(p?.stage ?? 1));
|
||||
if (p && (p.stage !== stage || (p as any).chapter !== undefined || (p as any).section !== undefined)) {
|
||||
this.data.stage_progress = { stage };
|
||||
}
|
||||
this.vmdata.stage_progress.stage = stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新关卡进度(权威源 data + 视图镜像 vmdata),并派发事件刷新 UI。
|
||||
* Why: 替代直接赋值 smc.data.stage_progress,保证视图层实时同步。
|
||||
* @param progress 新的关卡进度(章节/大关/小节均从 1 开始)
|
||||
* @param progress 新的关卡进度(仅关卡号,从 1 开始)
|
||||
* @param is_sync 是否标记脏并触发云端/本地同步
|
||||
*/
|
||||
public updateStageProgress(progress: { chapter: number; stage: number; section: number }, is_sync: boolean = true): void {
|
||||
this.data.stage_progress = { ...progress };
|
||||
public updateStageProgress(progress: { stage: number }, is_sync: boolean = true): void {
|
||||
this.data.stage_progress = { stage: Math.max(1, Math.floor(progress.stage)) };
|
||||
this.syncStageProgressToView();
|
||||
if (is_sync) {
|
||||
gameDataSync.markDataDirty();
|
||||
@@ -452,20 +446,18 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记大关已通关(通关点调用,覆盖存档停留场景)
|
||||
* Why: 第 10 关通关后存档停留 1-10-10,无法从 stage_progress 推导已通状态,
|
||||
* 需在通关时显式写入。
|
||||
* @param stage 大关号
|
||||
* 标记大关已通关(历史遗留入口)
|
||||
* Why: 旧版本第 10 关通关后存档停留,无法从 stage_progress 推导已通状态,需显式标记;
|
||||
* 无限关模式存档永远推进,判定已通可直接由 stage_progress 推导,无需再写入。
|
||||
* 保留空实现以避免破坏外部调用方(若有旧存档读取场景走 passed_stages 兼容)。
|
||||
* @param _stage 大关号(未使用,保留签名兼容)
|
||||
* @deprecated 无限关模式无需调用,保留仅为兼容旧代码引用
|
||||
*/
|
||||
public markStagePassed(stage: number): void {
|
||||
if (!this.data.passed_stages) this.data.passed_stages = [];
|
||||
if (!this.data.passed_stages.includes(stage)) {
|
||||
this.data.passed_stages.push(stage);
|
||||
gameDataSync.markDataDirty();
|
||||
}
|
||||
public markStagePassed(_stage: number): void {
|
||||
// 无限关模式不再写入 passed_stages
|
||||
}
|
||||
|
||||
/** 判定大关是否已通关:显式标记优先,存档推进兜底(已进入后续关即已通关) */
|
||||
/** 判定大关是否已通关:旧存档显式标记兼容,无限关以存档关卡号推导(已进入后续关即已通关) */
|
||||
private isStagePassed(stage: number): boolean {
|
||||
if ((this.data.passed_stages ?? []).includes(stage)) return true;
|
||||
return (this.data.stage_progress?.stage ?? 1) > stage;
|
||||
@@ -694,7 +686,7 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
/**
|
||||
* 消耗天赋点升级天赋(升级即激活,下一局生效)
|
||||
* @param uuid 天赋 uuid
|
||||
* @returns 是否升级成功(天赋不存在/已满级/阶位未解锁/点数不足时返回 false)
|
||||
* @returns 是否升级成功(天赋不存在/已满级/层位未解锁/前置未满足/点数不足时返回 false)
|
||||
*/
|
||||
upgradeTalent(uuid: number): boolean {
|
||||
const cfg = TalentSet[uuid];
|
||||
@@ -702,11 +694,13 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
if (!this.data.talents) this.data.talents = {};
|
||||
const curLv = this.getTalentLv(uuid);
|
||||
if (curLv >= cfg.max_lv) return false;
|
||||
// 阶位解锁:二/三阶天赋要求前一阶累计投入点数达标(一阶恒解锁)
|
||||
// 层位解锁:第 N 层天赋要求第 N-1 层累计投入点数达标(第 1 层恒解锁)
|
||||
if (!isTalentTierUnlocked(this.data.talents, cfg.tier)) return false;
|
||||
const cost = getTalentUpgradeCost(cfg.tier);
|
||||
if (cost < 0 || this.getTalentPoints() < cost) return false;
|
||||
this.data.talent_points -= cost;
|
||||
// 软前置:前置天赋等级需达标(仅限制加点)
|
||||
if (!isTalentPrereqMet(this.data.talents, cfg)) return false;
|
||||
// 统一 1 点/级
|
||||
if (this.getTalentPoints() < 1) return false;
|
||||
this.data.talent_points -= 1;
|
||||
this.data.talents[uuid] = curLv + 1;
|
||||
gameDataSync.markDataDirty();
|
||||
oops.message.dispatchEvent(GameEvent.TALENT_UPDATE);
|
||||
@@ -715,17 +709,13 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
|
||||
/**
|
||||
* 重置所有天赋等级并退还全部已消耗天赋点(看广告入口)
|
||||
* Why: 退还点数按"升到当前等级的累计消耗"计算,与升级消耗表严格对账,避免点数膨胀/缩水。
|
||||
* Why: 统一 1 点/级后退还点数 = 等级总和,与升级消耗严格对账,避免点数膨胀/缩水。
|
||||
*/
|
||||
resetTalents(): void {
|
||||
if (!this.data.talents) this.data.talents = {};
|
||||
let refund = 0;
|
||||
for (const key in this.data.talents) {
|
||||
const lv = this.data.talents[key];
|
||||
for (let i = 0; i < lv; i++) {
|
||||
const c = getTalentUpgradeCost(i);
|
||||
if (c > 0) refund += c;
|
||||
}
|
||||
refund += this.data.talents[key];
|
||||
}
|
||||
this.data.talents = {};
|
||||
this.data.talent_points = (this.data.talent_points ?? 0) + refund;
|
||||
|
||||
@@ -104,15 +104,15 @@ export enum GameEvent {
|
||||
STAGE_PROGRESS_UPDATE = "STAGE_PROGRESS_UPDATE",
|
||||
/** 奖励领取完成(payload 为空,UI 刷新待领取列表/红点) */
|
||||
REWARD_CLAIMED = "REWARD_CLAIMED",
|
||||
/** 小节开始(payload: { chapter, stage, section },MissionMonComp 据此生成怪物) */
|
||||
/** 回合开始(payload: { stage, section },MissionMonComp 据此生成怪物) */
|
||||
SectionStart = "SectionStart",
|
||||
/** 小节结束(payload: { chapter, stage, section },玩家点击"下一节"后推进) */
|
||||
/** @deprecated 已废弃:无限关模式无节间结算,回合自动推进,勿再监听 */
|
||||
SectionEnd = "SectionEnd",
|
||||
/** 大关结束(payload: { chapter, stage },10 节打完) */
|
||||
/** @deprecated 已废弃:无限关模式无大关结算,勿再监听 */
|
||||
StageEnd = "StageEnd",
|
||||
/** 章节结束(payload: { chapter },10 关打完) */
|
||||
/** @deprecated 已废弃:无限关模式无章节概念,勿再监听 */
|
||||
ChapterEnd = "ChapterEnd",
|
||||
/** 结算弹窗点击"下一节"(payload 为空,MissionComp 据此推进到下一小节) */
|
||||
/** @deprecated 已废弃:无限关模式无"下一节"手动推进,勿再监听 */
|
||||
NextSection = "NextSection",
|
||||
/** 结算弹窗点击"再来一次"(payload 为空,MissionComp 据此重开本关) */
|
||||
RestartMission = "RestartMission",
|
||||
|
||||
@@ -47,7 +47,7 @@ export enum FightSet {
|
||||
/** 英雄属性随等级成长的倍率(ap/hp = base * MULTIPLIER^(lv-1)) */
|
||||
HERO_LV_MULTIPLIER = 2,
|
||||
// BACK_RANG=30,//后退范围
|
||||
BACK_RANG = 20,//后退范围
|
||||
BACK_RANG = 30,//后退范围
|
||||
FiIGHT_TIME = 30,//战斗时间
|
||||
FROST_TIME = 3,//冰冻时间
|
||||
STUN_TIME = 1,//击晕时间(短控:施加瞬间 CD 削减,期间 CD 停止增长)
|
||||
|
||||
@@ -25,6 +25,10 @@ export enum UIID {
|
||||
Mission,
|
||||
/** 技能信息弹窗(技能小图标点击弹出) */
|
||||
SInfo,
|
||||
/** 英雄详情弹窗(图鉴卡片点击弹出:含属性/技能/升级/前后切换) */
|
||||
HeroInfo,
|
||||
/** 天赋详情弹窗(天赋迷你卡点击弹出:图标/等级/前置/条幅说明/加点按钮) */
|
||||
TIBox,
|
||||
}
|
||||
|
||||
/** 打开界面方式的配置数据 */
|
||||
@@ -41,5 +45,7 @@ export var UIConfigData: { [key: number]: UIConfig } = {
|
||||
// [UIID.Talents]: { layer: LayerType.UI, prefab: "gui/element/talents" },
|
||||
[UIID.Mission]: { layer: LayerType.UI, prefab: "gui/element/mission" },
|
||||
[UIID.SInfo]: { layer: LayerType.PopUp, prefab: "gui/element/SInfoBox" },
|
||||
[UIID.HeroInfo]: { layer: LayerType.PopUp, prefab: "gui/element/heroInfo", bundle: "resources" },
|
||||
[UIID.TIBox]: { layer: LayerType.PopUp, prefab: "gui/element/TIBox", bundle: "resources" },
|
||||
|
||||
}
|
||||
@@ -65,6 +65,16 @@ export enum EType {
|
||||
timeEnd = 1, //碰撞够也不消失,时间到才消失
|
||||
collision = 2, //碰撞次数够就消失
|
||||
}
|
||||
|
||||
/**
|
||||
* 召唤类技能(summon_count>1)多次施放的目标选择模式(由技能 prefab 上 SkillView 配置)。
|
||||
* 仅对敌方目标技能生效;友方/自身技能不走该逻辑。
|
||||
*/
|
||||
export enum SummonTMode {
|
||||
same = 0, // 同一敌人:所有召唤物锁定施法时选定的目标(默认,原行为)
|
||||
random = 1, // 随机敌人:每次出手时从射程内敌人中随机锁定一个
|
||||
unique = 2, // 避免相同敌人:优先锁定本轮尚未选过的敌人,一轮选完后重置再选
|
||||
}
|
||||
//debuff类型
|
||||
|
||||
|
||||
@@ -318,6 +328,21 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "红瓶抛射物",
|
||||
},
|
||||
6013: {
|
||||
uuid: 6013, name: "闪电", sp_name: "m3", icon: "109-Red-Potion", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.fixedEnd, EType: EType.collision, info: "闪电",
|
||||
},
|
||||
6014: {
|
||||
uuid: 6014, name: "小火球", sp_name: "m4", icon: "109-Red-Potion", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.fixedEnd, EType: EType.collision, info: "小火球",
|
||||
},
|
||||
6015: {
|
||||
uuid: 6015, name: "小飓风", sp_name: "m5", icon: "109-Red-Potion", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.fixedEnd, EType: EType.collision, info: "小飓风抛",
|
||||
},
|
||||
// ==================== 群体大招(按职业分段:6100-6149 坦克/战士、6150-6199 刺客/射手、6200-6249 法师/辅助) ====================
|
||||
// 坦克/战士群体攻击大招
|
||||
6100: {
|
||||
@@ -336,8 +361,8 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
RType: RType.bezier, EType: EType.collision, info: "火焰斩动画",//英雄配置技能的时候,可以叠加暴击debuff
|
||||
},
|
||||
6103: {
|
||||
uuid: 6103, name: "闪电斩", sp_name: "atk5", icon: "100-Enchant-Magic", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max0",
|
||||
DTType: DTType.single, para: 30, ap: 150, hit_count: 5, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
uuid: 6103, name: "闪电斩", sp_name: "atk6", icon: "100-Enchant-Magic", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max0",
|
||||
DTType: DTType.single, ap: 150, hit_count: 5, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "闪电斩动画",//英雄配置技能的时候,可以疲惫触发闪电系追加技能
|
||||
},
|
||||
6104: {
|
||||
@@ -358,39 +383,39 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
},
|
||||
// 法师/辅助群体攻击大招
|
||||
6200: {
|
||||
uuid: 6200, name: "火球术", sp_name: "maxm1", icon: "31-Fire", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max0",
|
||||
uuid: 6200, name: "火球术", sp_name: "maxm1", icon: "31-Fire", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.bezier, EType: EType.collision, bezier_start_y: 20, bezier_mid_y: 140, bezier_arc: 1.05, info: "一个大火球",
|
||||
},
|
||||
6201: {
|
||||
uuid: 6201, name: "飓风", sp_name: "maxm2", icon: "98-Enchant-Wind", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
DTType: DTType.single, ap: 100, hit_count: 99, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.linear, EType: EType.collision, info: "一个飓风",
|
||||
},
|
||||
6202: {
|
||||
uuid: 6202, name: "毒气弹", sp_name: "maxm4", icon: "o_warlock_hyzy_skill", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.linear, EType: EType.collision, bezier_start_y: 20, bezier_mid_y: 140, bezier_arc: 1.05, info: "一个大毒气弹",
|
||||
},
|
||||
6203: {
|
||||
uuid: 6203, name: "能量波", sp_name: "maxm5", icon: "b_priest_kx_skill", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.linear, EType: EType.collision, bezier_start_y: 20, bezier_mid_y: 140, bezier_arc: 1.05, info: "一个能量波",
|
||||
},
|
||||
6204: {
|
||||
uuid: 6204, name: "多毒气弹", sp_name: "maxm6", icon: "o_warlock_hyzy_skill", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
uuid: 6204, name: "雷暴", sp_name: "maxm7", icon: "o_warlock_hyzy_skill", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, ap: 100, hit_count: 99, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.linear, EType: EType.collision, bezier_start_y: 20, bezier_mid_y: 140, bezier_arc: 1.05, info: "一个大毒气弹",
|
||||
},
|
||||
|
||||
//============================= ====== 辅助技能 回合技能 ====== ==========================
|
||||
6301: {
|
||||
uuid: 6301, name: "护盾", sp_name: "buff_wind", icon: "28-Electric-Wave", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
uuid: 6301, name: "护盾", sp_name: "buff_wind", icon: "28-Electric-Wave", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, kind: SkillKind.Shield, ap: 30, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support, shield_duration: 7,
|
||||
RType: RType.fixed, EType: EType.animationEnd, info: "获得施法者最大生命30%的护盾, 持续7秒",
|
||||
},
|
||||
6302: {
|
||||
uuid: 6302, name: "治疗", sp_name: "buff_wind", icon: "g_priest_zldy_skill", TGroup: TGroup.Team, readyAnm: "up_green", endAnm: "", act: "atk",
|
||||
uuid: 6302, name: "治疗", sp_name: "buff_wind", icon: "g_priest_zldy_skill", TGroup: TGroup.Team, readyAnm: "up_green", endAnm: "", act: "max1",
|
||||
DTType: DTType.single, kind: SkillKind.Heal, ap: 300, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, info: "治疗伙伴/自己",
|
||||
},
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
/**
|
||||
* @file StageSet.ts
|
||||
* @description 关卡系统配置(章节-关-节三级结构)
|
||||
* @description 关卡系统配置(无限关卡·单线推进)
|
||||
*
|
||||
* 设计要点:
|
||||
* - 1 章 = 10 关,每关 = 5 小节(一关一局,节间自动推进),共 50 个小节
|
||||
* - 每关节点固定:1~3 节普通(3 小怪)、第 4 节精英(3 精英)、第 5 节 Boss(1 Boss + 2 小怪)
|
||||
* - 怪物等级 = 关卡号(1-1 的怪为 Lv1,1-5 的怪为 Lv5)
|
||||
* - 怪物属性 = 基础 × 开局系数 × (1 + 成长斜率 × (lv-1)) × 类型倍率
|
||||
* - 无限关卡:从第 1 关起无上限推进,每关 = 5 回合(一关一局,回合间自动推进)
|
||||
* - 第 1~4 回合为普通怪,属性逐回合轻量增强(SECTION_RAMP_STEP)
|
||||
* - 第 5 回合为单 Boss 回合(无护卫),Boss 采用超厚血量(BOSS_HP_MUL)
|
||||
* - 怪物等级 = 关卡号(第 N 关的怪为 LvN,随关卡无限成长)
|
||||
* - 小节配置按需动态生成,不再依赖章节静态表
|
||||
*
|
||||
* 数值基准:
|
||||
* - 开局系数 0.8(1 级小怪略弱于英雄)
|
||||
* - 成长斜率 +5%/级(10 级 ≈ 1.45 倍)
|
||||
* - 精英倍率 × 1.5,Boss 倍率 × 3.0(精英 × 2)
|
||||
* - 成长斜率 +5%/级(无限线性成长,曲线平缓可控)
|
||||
* - 回合增强 +12%/回合(第 4 回合 ≈ 1.36 倍)
|
||||
* - Boss:HP × 12(超厚血量)/ AP × 2.0(攻击克制,避免 Boss 秒杀英雄)
|
||||
*
|
||||
* 核心公式:
|
||||
* 怪物属性 = 基础 × MON_START_COEF × (1 + MON_GROW_SLOPE×(lv-1)) × 回合增强 × 类型倍率
|
||||
*/
|
||||
|
||||
import { MonType } from "./heroSet";
|
||||
@@ -20,82 +25,54 @@ import { MonType } from "./heroSet";
|
||||
|
||||
/** 怪物开局系数:1 级小怪略弱于英雄(0.8 倍) */
|
||||
export const MON_START_COEF = 0.8;
|
||||
/** 怪物成长斜率:每级 +5% 基础属性 */
|
||||
/** 怪物成长斜率:每级 +5% 基础属性(无限线性成长) */
|
||||
export const MON_GROW_SLOPE = 0.05;
|
||||
/** 精英属性倍率:同级普通怪的 1.5 倍 */
|
||||
export const ELITE_MUL = 1.5;
|
||||
/** Boss 属性倍率:同级普通怪的 3.0 倍(精英 × 2) */
|
||||
export const BOSS_MUL = 3.0;
|
||||
/** 每关小节总数(一关一局,节间自动推进) */
|
||||
/** 回合内轻量增强步长:第 1→4 回合每回合 +12% 属性 */
|
||||
export const SECTION_RAMP_STEP = 0.12;
|
||||
/** Boss 血量倍率:同级普通怪的 12 倍(超厚血量,拖长 Boss 战节奏) */
|
||||
export const BOSS_HP_MUL = 12;
|
||||
/** Boss 攻击倍率:同级普通怪的 2.0 倍(显著低于血量倍率,避免 Boss 秒杀英雄) */
|
||||
export const BOSS_AP_MUL = 2.0;
|
||||
/** 每关回合总数(一关一局,回合间自动推进) */
|
||||
export const SECTIONS_PER_STAGE = 5;
|
||||
/** 精英节号(每关第 4 节为精英节) */
|
||||
export const ELITE_SECTION = 4;
|
||||
/** Boss 节号(每关第 5 节为 Boss 节,即末节) */
|
||||
/** Boss 回合号(每关第 5 回合,即末回合) */
|
||||
export const BOSS_SECTION = 5;
|
||||
/** 普通节小怪数量(一关一局压缩后单节 3 只) */
|
||||
/** 普通回合小怪数量 */
|
||||
export const NORMAL_MON_COUNT = 3;
|
||||
/** 普通节开局立即刷出数量(3 只全部立即刷出) */
|
||||
/** 普通回合开局立即刷出数量(3 只全部立即刷出) */
|
||||
export const NORMAL_FIRST_SPAWN = 3;
|
||||
/** 普通节后续刷怪间隔(秒):保留配置位(3 只全立即刷,当前无后续批次) */
|
||||
/** 普通回合后续刷怪间隔(秒):保留配置位(3 只全立即刷,当前无后续批次) */
|
||||
export const NORMAL_SPAWN_INTERVAL = 2;
|
||||
/** 精英节精英数量 */
|
||||
export const ELITE_COUNT = 3;
|
||||
/** Boss 节护卫小怪数量 */
|
||||
export const BOSS_ELITE_COUNT = 2;
|
||||
/** Boss 节 Boss 延迟刷出时间(秒) */
|
||||
/** Boss 回合 Boss 延迟刷出时间(秒) */
|
||||
export const BOSS_DELAY = 1;
|
||||
|
||||
// ======================== 类型定义 ========================
|
||||
|
||||
/** 小节类型 */
|
||||
/** 回合类型 */
|
||||
export enum SectionType {
|
||||
Normal = 0, // 普通节:3 小怪
|
||||
Elite = 1, // 精英节:3 精英
|
||||
Boss = 2, // Boss 节:1 Boss + 2 小怪
|
||||
Normal = 0, // 普通回合:3 小怪(逐回合轻量增强)
|
||||
Boss = 2, // Boss 回合:1 Boss(超厚血量,无护卫)
|
||||
}
|
||||
|
||||
/** 小节配置 */
|
||||
/** 回合配置 */
|
||||
export interface SectionConfig {
|
||||
/** 小节类型 */
|
||||
/** 回合类型 */
|
||||
type: SectionType;
|
||||
/** 怪物等级(驱动线性成长) */
|
||||
/** 怪物等级(= 关卡号,驱动线性成长) */
|
||||
monLv: number;
|
||||
/** 精英数量(精英关/Boss 关用) */
|
||||
eliteCount?: number;
|
||||
/** Boss UUID(Boss 关用,可选,缺省随机) */
|
||||
bossUuid?: number;
|
||||
/** 怪物类型池(普通关用,缺省随机) */
|
||||
/** 回合增强系数(第 n 回合 = 1 + SECTION_RAMP_STEP×(n-1),仅普通回合生效) */
|
||||
ramp: number;
|
||||
/** 怪物类型池(普通回合用,缺省随机) */
|
||||
monTypes?: MonType[];
|
||||
}
|
||||
|
||||
/** 大关配置(1-1 ~ 1-10),每关 SECTIONS_PER_STAGE 个小节 */
|
||||
export interface StageConfig {
|
||||
/** 章节号 */
|
||||
chapter: number;
|
||||
/** 关卡号 1~10 */
|
||||
stage: number;
|
||||
/** 小节列表(长度 = SECTIONS_PER_STAGE) */
|
||||
sections: SectionConfig[];
|
||||
}
|
||||
|
||||
/** 章节配置 */
|
||||
export interface ChapterConfig {
|
||||
/** 章节号 */
|
||||
chapter: number;
|
||||
/** 章节名称 */
|
||||
name: string;
|
||||
/** 10 个大关 */
|
||||
stages: StageConfig[];
|
||||
}
|
||||
|
||||
// ======================== 工具函数 ========================
|
||||
|
||||
/**
|
||||
* 计算怪物最终属性
|
||||
* 计算怪物最终属性(Boss 血/攻倍率分离:超厚血 + 克制攻)
|
||||
* @param baseHp 基础 HP
|
||||
* @param baseAp 基础 AP
|
||||
* @param lv 怪物等级
|
||||
* @param isElite 是否精英
|
||||
* @param isBoss 是否 Boss
|
||||
* @returns 最终 HP/AP
|
||||
*/
|
||||
@@ -103,159 +80,70 @@ export function calcMonsterAttrs(
|
||||
baseHp: number,
|
||||
baseAp: number,
|
||||
lv: number,
|
||||
isElite: boolean = false,
|
||||
isBoss: boolean = false
|
||||
): { hp: number; ap: number } {
|
||||
const growCoef = 1 + MON_GROW_SLOPE * (lv - 1);
|
||||
let typeMul = 1;
|
||||
if (isBoss) {
|
||||
typeMul = BOSS_MUL;
|
||||
} else if (isElite) {
|
||||
typeMul = ELITE_MUL;
|
||||
}
|
||||
const hp = Math.max(1, Math.round(baseHp * MON_START_COEF * growCoef * typeMul));
|
||||
const ap = Math.max(1, Math.round(baseAp * MON_START_COEF * growCoef * typeMul));
|
||||
const hp = Math.max(1, Math.round(baseHp * MON_START_COEF * growCoef * (isBoss ? BOSS_HP_MUL : 1)));
|
||||
const ap = Math.max(1, Math.round(baseAp * MON_START_COEF * growCoef * (isBoss ? BOSS_AP_MUL : 1)));
|
||||
return { hp, ap };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定关卡的怪物等级
|
||||
* @param stage 关卡号(1~10)
|
||||
* 获取指定关卡的怪物等级(无限关:等级 = 关卡号,无上限)
|
||||
* @param stage 关卡号(1~∞)
|
||||
* @returns 怪物等级
|
||||
*/
|
||||
export function getMonsterLv(stage: number): number {
|
||||
return Math.max(1, Math.min(10, stage));
|
||||
return Math.max(1, Math.floor(stage));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为精英节(每关第 ELITE_SECTION 节)
|
||||
* @param section 小节号(1~SECTIONS_PER_STAGE)
|
||||
*/
|
||||
export function isEliteSection(section: number): boolean {
|
||||
return section === ELITE_SECTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为 Boss 节(每关第 BOSS_SECTION 节,即末节)
|
||||
* @param section 小节号(1~SECTIONS_PER_STAGE)
|
||||
* 判断是否为 Boss 回合(每关第 BOSS_SECTION 回合,即末回合)
|
||||
* @param section 回合号(1~SECTIONS_PER_STAGE)
|
||||
*/
|
||||
export function isBossSection(section: number): boolean {
|
||||
return section === BOSS_SECTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小节类型(按节号区分:1~3 普通 / 4 精英 / 5 Boss)
|
||||
* @param section 小节号(1~SECTIONS_PER_STAGE)
|
||||
* 获取回合类型(按回合号区分:1~4 普通 / 5 Boss)
|
||||
* @param section 回合号(1~SECTIONS_PER_STAGE)
|
||||
*/
|
||||
export function getSectionType(section: number): SectionType {
|
||||
if (isBossSection(section)) return SectionType.Boss;
|
||||
if (isEliteSection(section)) return SectionType.Elite;
|
||||
return SectionType.Normal;
|
||||
return isBossSection(section) ? SectionType.Boss : SectionType.Normal;
|
||||
}
|
||||
|
||||
// ======================== 第 1 章配置 ========================
|
||||
|
||||
/** 生成单个大关的 SECTIONS_PER_STAGE 个小节配置(1~3 普通 / 4 精英 / 5 Boss) */
|
||||
function makeStageSections(stage: number): SectionConfig[] {
|
||||
const monLv = getMonsterLv(stage);
|
||||
const sections: SectionConfig[] = [];
|
||||
|
||||
for (let i = 1; i <= SECTIONS_PER_STAGE; i++) {
|
||||
const type = getSectionType(i);
|
||||
const section: SectionConfig = { type, monLv };
|
||||
|
||||
if (type === SectionType.Elite) {
|
||||
section.eliteCount = ELITE_COUNT;
|
||||
} else if (type === SectionType.Boss) {
|
||||
section.eliteCount = BOSS_ELITE_COUNT;
|
||||
// bossUuid 缺省随机,由生成引擎从 Boss 池抽取
|
||||
} else {
|
||||
// 普通节:近战为主,穿插远程/重型
|
||||
section.monTypes = [MonType.Melee, MonType.Melee, MonType.Melee, MonType.Heavy, MonType.Long];
|
||||
}
|
||||
|
||||
sections.push(section);
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
/** 第 1 章配置 */
|
||||
export const Chapter1: ChapterConfig = {
|
||||
chapter: 1,
|
||||
name: "兽人入侵",
|
||||
stages: [],
|
||||
};
|
||||
|
||||
// 初始化第 1 章 10 个大关
|
||||
for (let stage = 1; stage <= 10; stage++) {
|
||||
Chapter1.stages.push({
|
||||
chapter: 1,
|
||||
stage,
|
||||
sections: makeStageSections(stage),
|
||||
});
|
||||
}
|
||||
|
||||
/** 所有章节配置表(当前仅第 1 章,后续扩展) */
|
||||
export const ChapterSet: Record<number, ChapterConfig> = {
|
||||
1: Chapter1,
|
||||
};
|
||||
|
||||
// ======================== 查询接口 ========================
|
||||
|
||||
/**
|
||||
* 获取小节配置
|
||||
* @param chapter 章节号
|
||||
* @param stage 关卡号
|
||||
* @param section 小节号(1~SECTIONS_PER_STAGE)
|
||||
* @returns 小节配置,不存在时返回 null
|
||||
* 获取回合配置(无限关:按需动态生成,不再依赖章节静态表)
|
||||
* @param stage 关卡号(1~∞)
|
||||
* @param section 回合号(1~SECTIONS_PER_STAGE)
|
||||
* @returns 回合配置,回合号越界时返回 null
|
||||
*/
|
||||
export function getSectionConfig(chapter: number, stage: number, section: number): SectionConfig | null {
|
||||
const ch = ChapterSet[chapter];
|
||||
if (!ch) return null;
|
||||
const st = ch.stages.find(s => s.stage === stage);
|
||||
if (!st) return null;
|
||||
if (section < 1 || section > st.sections.length) return null;
|
||||
return st.sections[section - 1];
|
||||
export function getSectionConfig(stage: number, section: number): SectionConfig | null {
|
||||
if (section < 1 || section > SECTIONS_PER_STAGE) return null;
|
||||
|
||||
const type = getSectionType(section);
|
||||
const cfg: SectionConfig = {
|
||||
type,
|
||||
monLv: getMonsterLv(stage),
|
||||
ramp: 1 + SECTION_RAMP_STEP * (section - 1),
|
||||
};
|
||||
|
||||
if (type !== SectionType.Boss) {
|
||||
// 普通回合:近战为主,穿插远程/重型
|
||||
cfg.monTypes = [MonType.Melee, MonType.Melee, MonType.Melee, MonType.Heavy, MonType.Long];
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取大关配置
|
||||
* @param chapter 章节号
|
||||
* 获取当前进度显示文本(无限关:只展示 关-回合)
|
||||
* @param stage 关卡号
|
||||
* @returns 大关配置,不存在时返回 null
|
||||
* @param section 回合号
|
||||
* @returns 格式化文本,如 "5-3"
|
||||
*/
|
||||
export function getStageConfig(chapter: number, stage: number): StageConfig | null {
|
||||
const ch = ChapterSet[chapter];
|
||||
if (!ch) return null;
|
||||
return ch.stages.find(s => s.stage === stage) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取章节配置
|
||||
* @param chapter 章节号
|
||||
* @returns 章节配置,不存在时返回 null
|
||||
*/
|
||||
export function getChapterConfig(chapter: number): ChapterConfig | null {
|
||||
return ChapterSet[chapter] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前进度显示文本
|
||||
* @param chapter 章节号
|
||||
* @param stage 关卡号
|
||||
* @param section 小节号
|
||||
* @returns 格式化文本,如 "1-5-3"
|
||||
*/
|
||||
export function getProgressText(chapter: number, stage: number, section: number): string {
|
||||
return `${chapter}-${stage}-${section}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取大关显示文本
|
||||
* @param chapter 章节号
|
||||
* @param stage 关卡号
|
||||
* @returns 格式化文本,如 "1-5"
|
||||
*/
|
||||
export function getStageText(chapter: number, stage: number): string {
|
||||
return `${chapter}-${stage}`;
|
||||
export function getProgressText(stage: number, section: number): string {
|
||||
return `${stage}-${section}`;
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ import { HRole } from "./heroSet";
|
||||
/**
|
||||
* 天赋系统配置(局外成长:升级即激活,战斗中对英雄属性常驻加成)。
|
||||
*
|
||||
* 三阶结构(阶位逐级解锁,门槛见 TALENT_TIER_UNLOCK_THRESHOLD):
|
||||
* - 一阶·基础天赋(9001-9099):全局生效,对所有英雄叠加;
|
||||
* - 二阶·专精天赋(9101-9199):按 HRole 职业过滤,仅对匹配职业的英雄生效;
|
||||
* - 三阶·超强天赋(9201-9219):全局(9201-9209)+ 职业(9211-9219)混合,数值幅度约为前两阶的 2 倍。
|
||||
* 五层递进结构(逐层解锁,门槛见 TALENT_TIER_UNLOCK_THRESHOLD):
|
||||
* - 第 1 层(9001-9099):恒解锁,建议放置全局基础属性;
|
||||
* - 第 2 层(9101-9199):第 1 层累计投入 ≥5 点解锁;
|
||||
* - 第 3 层(9201-9299):第 2 层累计投入 ≥5 点解锁;
|
||||
* - 第 4 层(9301-9399):第 3 层累计投入 ≥5 点解锁;
|
||||
* - 第 5 层(9401-9499):第 4 层累计投入 ≥5 点解锁。
|
||||
*
|
||||
* 解锁规则:一阶恒可升级;二阶需一阶累计投入 ≥10 点;三阶需二阶累计投入 ≥10 点。
|
||||
* 专精/超强职业天赋不再有独立前置天赋,仅受阶位解锁约束。
|
||||
* 解锁规则:第 1 层恒可升级;第 N 层需第 N-1 层累计投入 ≥5 点。
|
||||
* 前置天赋(prereq_uuid/prereq_lv)为软前置:仅限制加点,不影响已有点数的战斗结算。
|
||||
*
|
||||
* 结算口径(与项目 buff 规范一致):
|
||||
* - percent 模式:按英雄基础值(base_ap/base_hp/hero.def)相对放大;
|
||||
@@ -23,14 +25,18 @@ export enum TalentValueMode {
|
||||
Flat = 1,
|
||||
}
|
||||
|
||||
/** 天赋阶位:一阶基础 / 二阶专精 / 三阶超强 */
|
||||
/** 天赋层位:第 1 层 ~ 第 5 层递进解锁 */
|
||||
export enum TalentTier {
|
||||
/** 一阶:基础天赋,全局生效 */
|
||||
Basic = 1,
|
||||
/** 二阶:专精天赋,按职业过滤 */
|
||||
Specialized = 2,
|
||||
/** 三阶:超强天赋,全局 + 职业混合 */
|
||||
Super = 3,
|
||||
/** 第 1 层:恒解锁 */
|
||||
L1 = 1,
|
||||
/** 第 2 层:第 1 层投入 5 点解锁 */
|
||||
L2 = 2,
|
||||
/** 第 3 层:第 2 层投入 5 点解锁 */
|
||||
L3 = 3,
|
||||
/** 第 4 层:第 3 层投入 5 点解锁 */
|
||||
L4 = 4,
|
||||
/** 第 5 层:第 4 层投入 5 点解锁 */
|
||||
L5 = 5,
|
||||
}
|
||||
|
||||
/** 单条属性加成词条 */
|
||||
@@ -47,7 +53,7 @@ export interface TalentAttrBonus {
|
||||
export interface TalentConfig {
|
||||
/** 天赋 uuid(段位见 TalentSet 注释) */
|
||||
uuid: number;
|
||||
/** 所属阶位(升级校验:升下一阶天赋需前一阶累计投入点数达标) */
|
||||
/** 所属层位(升级校验:升下一层天赋需前一层累计投入点数达标) */
|
||||
tier: TalentTier;
|
||||
/** 天赋名称 */
|
||||
name: string;
|
||||
@@ -59,8 +65,16 @@ export interface TalentConfig {
|
||||
bonuses: TalentAttrBonus[];
|
||||
/** 最大等级 */
|
||||
max_lv: number;
|
||||
/** 专精/超强职业天赋:仅对该职业英雄生效;全局天赋省略此字段 */
|
||||
/** 专精/职业天赋:仅对该职业英雄生效;全局天赋省略此字段 */
|
||||
role?: HRole;
|
||||
/** 软前置:前置天赋 uuid(与 prereq_lv 成对出现;缺省视为无前置) */
|
||||
prereq_uuid?: number;
|
||||
/** 软前置:前置天赋要求达到的等级(仅限制加点,不影响已有点数的结算) */
|
||||
prereq_lv?: number;
|
||||
/** L4 触发修正:触发类技能计数阈值减少量(每级,下限 1;与 bonuses 互斥) */
|
||||
t_num_reduce_per_lv?: number;
|
||||
/** L5 触发修正:触发类技能额外触发 1 次的概率(每级百分点;与 bonuses 互斥) */
|
||||
extra_trigger_chance_per_lv?: number;
|
||||
}
|
||||
|
||||
/** 单条已解析的加成(等级已乘算) */
|
||||
@@ -84,208 +98,201 @@ export interface ITalentTarget {
|
||||
base_ap: number;
|
||||
/** 基础血量快照(percent 模式的基数) */
|
||||
base_hp: number;
|
||||
/** L4 写入:触发类技能计数阈值减少量 */
|
||||
talent_t_num_reduce: number;
|
||||
/** L5 写入:触发类技能额外触发 1 次的概率(百分点) */
|
||||
talent_extra_trigger_chance: number;
|
||||
/** 防御基础值(percent 模式的基数,等于 hero.def) */
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 天赋配置表。
|
||||
* uuid 段位:9001-9099 一阶基础(全局);9101-9199 二阶专精(按职业);
|
||||
* 9201-9209 三阶超强·全局;9211-9219 三阶超强·职业。
|
||||
* uuid 段位:9001-9099 第 1 层;9101-9199 第 2 层;9201-9299 第 3 层;
|
||||
* 9301-9399 第 4 层;9401-9499 第 5 层。
|
||||
* 每层建议放置 4-6 个天赋;前置天赋用 prereq_uuid + prereq_lv 声明(软前置,仅限制加点)。
|
||||
* icon 复用 SkillSet/uiconsAtlas 既有帧名,后续可在编辑器侧替换为专属图标。
|
||||
*/
|
||||
export const TalentSet: Record<number, TalentConfig> = {
|
||||
// ==================== 一阶·基础天赋(9001-9099,全局生效) ====================
|
||||
// ==================== 第 1 层(9001-9099,恒解锁,基础属性) ====================
|
||||
9001: {
|
||||
uuid: 9001, tier: TalentTier.Basic, name: "攻击强化", info: "所有英雄攻击 +{value}%", icon: "3-Aggression",
|
||||
uuid: 9001, tier: TalentTier.L1, name: "攻击强化", info: "所有英雄攻击 +{value}%", icon: "3-Aggression",
|
||||
bonuses: [{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 3 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9002: {
|
||||
uuid: 9002, tier: TalentTier.Basic, name: "生命强化", info: "所有英雄生命 +{value}%", icon: "29-Endure",
|
||||
uuid: 9002, tier: TalentTier.L1, name: "生命强化", info: "所有英雄生命 +{value}%", icon: "29-Endure",
|
||||
bonuses: [{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 5 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9003: {
|
||||
uuid: 9003, tier: TalentTier.Basic, name: "防御强化", info: "所有英雄防御 +{value} 点", icon: "46-Iron-Clad",
|
||||
uuid: 9003, tier: TalentTier.L1, name: "防御强化", info: "所有英雄防御 +{value} 点", icon: "46-Iron-Clad",
|
||||
bonuses: [{ attr: Attrs.defense, mode: TalentValueMode.Flat, per_lv: 8 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9004: {
|
||||
uuid: 9004, tier: TalentTier.Basic, name: "暴击强化", info: "所有英雄暴击率 +{value}%", icon: "27-Hawkeye",
|
||||
uuid: 9004, tier: TalentTier.L1, name: "反伤荆棘", info: "所有英雄反伤 +{value} 点", icon: "45-Induration",
|
||||
bonuses: [{ attr: Attrs.thorns_dmg, mode: TalentValueMode.Flat, per_lv: 5 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
|
||||
// ==================== 第 2 层(9101-9199,第 1 层投入 5 点解锁,概率属性) ====================
|
||||
9101: {
|
||||
uuid: 9101, tier: TalentTier.L2, name: "暴击强化", info: "所有英雄暴击率 +{value}%", icon: "27-Hawkeye",
|
||||
bonuses: [{ attr: Attrs.critical, mode: TalentValueMode.Flat, per_lv: 2 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9005: {
|
||||
uuid: 9005, tier: TalentTier.Basic, name: "击晕强化", info: "所有英雄击晕率 +{value}%", icon: "13-Concussion",
|
||||
bonuses: [{ attr: Attrs.stun_chance, mode: TalentValueMode.Flat, per_lv: 2 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9006: {
|
||||
uuid: 9006, tier: TalentTier.Basic, name: "冰冻强化", info: "所有英雄冰冻率 +{value}%", icon: "92-Frost-Bite",
|
||||
bonuses: [{ attr: Attrs.freeze_chance, mode: TalentValueMode.Flat, per_lv: 2 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9007: {
|
||||
uuid: 9007, tier: TalentTier.Basic, name: "击退强化", info: "所有英雄击退距离 +{value}", icon: "34-Force-Push",
|
||||
9102: {
|
||||
uuid: 9102, tier: TalentTier.L2, name: "击退强化", info: "所有英雄击退距离 +{value}", icon: "34-Force-Push",
|
||||
bonuses: [{ attr: Attrs.knockback_distance, mode: TalentValueMode.Flat, per_lv: 10 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
// 风怒机制已停用: 9008 风怒强化天赋已移除
|
||||
/*
|
||||
9008: {
|
||||
uuid: 9008, tier: TalentTier.Basic, name: "风怒强化", info: "所有英雄风怒率 +{value}%", icon: "98-Enchant-Wind",
|
||||
bonuses: [{ attr: Attrs.wfuny, mode: TalentValueMode.Flat, per_lv: 2 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
*/
|
||||
|
||||
// ==================== 二阶·专精天赋(9101-9199,按职业过滤;一阶投入 10 点解锁) ====================
|
||||
9101: {
|
||||
uuid: 9101, tier: TalentTier.Specialized, name: "坦克专精", info: "坦克防御 +{value}%、生命 +{value2}%", icon: "36-Guardian-Stance",
|
||||
bonuses: [
|
||||
{ attr: Attrs.defense, mode: TalentValueMode.Percent, per_lv: 30 },
|
||||
{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 5 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Tank,
|
||||
},
|
||||
9102: {
|
||||
uuid: 9102, tier: TalentTier.Specialized, name: "战士专精", info: "战士生命 +{value}%", icon: "1-Battle-Roar",
|
||||
bonuses: [{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 12 }],
|
||||
max_lv: 5, role: HRole.Warrior,
|
||||
},
|
||||
9103: {
|
||||
uuid: 9103, tier: TalentTier.Specialized, name: "刺客专精", info: "刺客攻击 +{value}%、暴击率 +{value2}%", icon: "127-Hide",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.critical, mode: TalentValueMode.Flat, per_lv: 2 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Assassin,
|
||||
uuid: 9103, tier: TalentTier.L2, name: "冰冻强化", info: "所有英雄冰冻率 +{value}%", icon: "92-Frost-Bite",
|
||||
bonuses: [{ attr: Attrs.freeze_chance, mode: TalentValueMode.Flat, per_lv: 2 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9104: {
|
||||
uuid: 9104, tier: TalentTier.Specialized, name: "射手专精", info: "射手攻击 +{value}%、穿透率 +{value2}%", icon: "56-Pierce-Shot",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.puncture_chance, mode: TalentValueMode.Flat, per_lv: 2 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Archer,
|
||||
uuid: 9104, tier: TalentTier.L2, name: "击晕强化", info: "所有英雄击晕率 +{value}%", icon: "13-Concussion",
|
||||
bonuses: [{ attr: Attrs.stun_chance, mode: TalentValueMode.Flat, per_lv: 2 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9105: {
|
||||
uuid: 9105, tier: TalentTier.Specialized, name: "法师专精", info: "法师攻击 +{value}%、冰冻率 +{value2}%", icon: "145-Electromancy",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.freeze_chance, mode: TalentValueMode.Flat, per_lv: 2 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Mage,
|
||||
uuid: 9105, tier: TalentTier.L2, name: "穿透强化", info: "所有英雄穿透率 +{value}%", icon: "56-Pierce-Shot",
|
||||
bonuses: [{ attr: Attrs.puncture_chance, mode: TalentValueMode.Flat, per_lv: 2 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
9106: {
|
||||
uuid: 9106, tier: TalentTier.Specialized, name: "辅助专精", info: "辅助治疗量 +{value}%、施加的增益效果 +{value2}%", icon: "38-Healing-Light",
|
||||
bonuses: [
|
||||
{ attr: Attrs.heal_power, mode: TalentValueMode.Flat, per_lv: 10 },
|
||||
{ attr: Attrs.buff_power, mode: TalentValueMode.Flat, per_lv: 8 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Support,
|
||||
uuid: 9106, tier: TalentTier.L2, name: "暴伤强化", info: "所有英雄暴击伤害 +{value}%", icon: "37-Head-Shot",
|
||||
bonuses: [{ attr: Attrs.critical_damage, mode: TalentValueMode.Flat, per_lv: 5 }],
|
||||
max_lv: 5,
|
||||
},
|
||||
|
||||
// ==================== 三阶·超强天赋(9201-9219,二阶投入 10 点解锁;幅度约为前两阶 2 倍) ====================
|
||||
// ---- 全局超强(9201-9209) ----
|
||||
// ==================== 第 3 层(9201-9299,第 2 层投入 5 点解锁,职业加成) ====================
|
||||
9201: {
|
||||
uuid: 9201, tier: TalentTier.Super, name: "神威降临", info: "所有英雄攻击 +{value}%、生命 +{value2}%", icon: "52-Might-of-Heaven",
|
||||
uuid: 9201, tier: TalentTier.L3, name: "坦克专精", info: "坦克生命 +{value}%、防御 +{value2} 点", icon: "36-Guardian-Stance",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
],
|
||||
max_lv: 5,
|
||||
},
|
||||
9202: {
|
||||
uuid: 9202, tier: TalentTier.Super, name: "致命终结", info: "所有英雄暴击率 +{value}%、暴击伤害 +{value2}%", icon: "37-Head-Shot",
|
||||
bonuses: [
|
||||
{ attr: Attrs.critical, mode: TalentValueMode.Flat, per_lv: 3 },
|
||||
{ attr: Attrs.critical_damage, mode: TalentValueMode.Flat, per_lv: 15 },
|
||||
],
|
||||
max_lv: 5,
|
||||
},
|
||||
9203: {
|
||||
uuid: 9203, tier: TalentTier.Super, name: "破甲重创", info: "所有英雄穿透率 +{value}%、穿透伤害 +{value2}%", icon: "76-Shove-Stab",
|
||||
bonuses: [
|
||||
{ attr: Attrs.puncture_chance, mode: TalentValueMode.Flat, per_lv: 3 },
|
||||
{ attr: Attrs.puncture_dmg_bonus, mode: TalentValueMode.Flat, per_lv: 10 },
|
||||
],
|
||||
max_lv: 5,
|
||||
},
|
||||
9204: {
|
||||
uuid: 9204, tier: TalentTier.Super, name: "钢铁之躯", info: "所有英雄防御 +{value}%、生命 +{value2}%", icon: "45-Induration",
|
||||
bonuses: [
|
||||
{ attr: Attrs.defense, mode: TalentValueMode.Percent, per_lv: 20 },
|
||||
{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 6 },
|
||||
],
|
||||
max_lv: 5,
|
||||
},
|
||||
// ---- 职业超强(9211-9219,按 HRole 过滤) ----
|
||||
9211: {
|
||||
uuid: 9211, tier: TalentTier.Super, name: "不朽壁垒", info: "坦克防御 +{value}%、生命 +{value2}%", icon: "36-Guardian-Stance",
|
||||
bonuses: [
|
||||
{ attr: Attrs.defense, mode: TalentValueMode.Percent, per_lv: 40 },
|
||||
{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.defense, mode: TalentValueMode.Flat, per_lv: 10 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Tank,
|
||||
},
|
||||
9212: {
|
||||
uuid: 9212, tier: TalentTier.Super, name: "狂血战魂", info: "战士攻击 +{value}%、生命 +{value2}%", icon: "113-Rage",
|
||||
9202: {
|
||||
uuid: 9202, tier: TalentTier.L3, name: "战士专精", info: "战士攻击 +{value}%、生命 +{value2}%", icon: "1-Battle-Roar",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 10 },
|
||||
{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 6 },
|
||||
{ attr: Attrs.hp_max, mode: TalentValueMode.Percent, per_lv: 6 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Warrior,
|
||||
},
|
||||
9213: {
|
||||
uuid: 9213, tier: TalentTier.Super, name: "影刃绝杀", info: "刺客攻击 +{value}%、暴击伤害 +{value2}%", icon: "136-Heart-Rip",
|
||||
9203: {
|
||||
uuid: 9203, tier: TalentTier.L3, name: "刺客专精", info: "刺客攻击 +{value}%、攻速 +{value2}%", icon: "127-Hide",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 12 },
|
||||
{ attr: Attrs.critical_damage, mode: TalentValueMode.Flat, per_lv: 12 },
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.speed, mode: TalentValueMode.Flat, per_lv: 3 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Assassin,
|
||||
},
|
||||
9214: {
|
||||
uuid: 9214, tier: TalentTier.Super, name: "穿云贯日", info: "射手攻击 +{value}%、穿透伤害 +{value2}%", icon: "37-Head-Shot",
|
||||
9204: {
|
||||
uuid: 9204, tier: TalentTier.L3, name: "射手专精", info: "射手攻击 +{value}%、攻速 +{value2}%", icon: "56-Pierce-Shot",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 12 },
|
||||
{ attr: Attrs.puncture_dmg_bonus, mode: TalentValueMode.Flat, per_lv: 8 },
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.speed, mode: TalentValueMode.Flat, per_lv: 3 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Archer,
|
||||
},
|
||||
9215: {
|
||||
uuid: 9215, tier: TalentTier.Super, name: "元素主宰", info: "法师攻击 +{value}%、冰冻率 +{value2}%", icon: "144-Cryomancy",
|
||||
9205: {
|
||||
uuid: 9205, tier: TalentTier.L3, name: "法师专精", info: "法师攻击 +{value}%、技能增益效果 +{value2}%", icon: "145-Electromancy",
|
||||
bonuses: [
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 12 },
|
||||
{ attr: Attrs.freeze_chance, mode: TalentValueMode.Flat, per_lv: 3 },
|
||||
{ attr: Attrs.ap, mode: TalentValueMode.Percent, per_lv: 8 },
|
||||
{ attr: Attrs.buff_power, mode: TalentValueMode.Flat, per_lv: 6 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Mage,
|
||||
},
|
||||
9216: {
|
||||
uuid: 9216, tier: TalentTier.Super, name: "圣光普照", info: "辅助治疗量 +{value}%、施加的增益效果 +{value2}%", icon: "40-Holy",
|
||||
9206: {
|
||||
uuid: 9206, tier: TalentTier.L3, name: "辅助专精", info: "辅助治疗量 +{value}%、增益效果 +{value2}%", icon: "38-Healing-Light",
|
||||
bonuses: [
|
||||
{ attr: Attrs.heal_power, mode: TalentValueMode.Flat, per_lv: 15 },
|
||||
{ attr: Attrs.buff_power, mode: TalentValueMode.Flat, per_lv: 12 },
|
||||
{ attr: Attrs.heal_power, mode: TalentValueMode.Flat, per_lv: 10 },
|
||||
{ attr: Attrs.buff_power, mode: TalentValueMode.Flat, per_lv: 6 },
|
||||
],
|
||||
max_lv: 5, role: HRole.Support,
|
||||
},
|
||||
|
||||
// ==================== 第 4 层(9301-9399,第 3 层投入 5 点解锁,触发条件减少,按职业) ====================
|
||||
9301: {
|
||||
uuid: 9301, tier: TalentTier.L4, name: "坦克·蓄势", info: "坦克触发类技能所需次数 -{value}", icon: "36-Guardian-Stance",
|
||||
bonuses: [], t_num_reduce_per_lv: 1,
|
||||
max_lv: 2, role: HRole.Tank,
|
||||
},
|
||||
9302: {
|
||||
uuid: 9302, tier: TalentTier.L4, name: "战士·蓄势", info: "战士触发类技能所需次数 -{value}", icon: "113-Rage",
|
||||
bonuses: [], t_num_reduce_per_lv: 1,
|
||||
max_lv: 2, role: HRole.Warrior,
|
||||
},
|
||||
9303: {
|
||||
uuid: 9303, tier: TalentTier.L4, name: "刺客·蓄势", info: "刺客触发类技能所需次数 -{value}", icon: "136-Heart-Rip",
|
||||
bonuses: [], t_num_reduce_per_lv: 1,
|
||||
max_lv: 2, role: HRole.Assassin,
|
||||
},
|
||||
9304: {
|
||||
uuid: 9304, tier: TalentTier.L4, name: "射手·蓄势", info: "射手触发类技能所需次数 -{value}", icon: "74-Satisfaction",
|
||||
bonuses: [], t_num_reduce_per_lv: 1,
|
||||
max_lv: 2, role: HRole.Archer,
|
||||
},
|
||||
9305: {
|
||||
uuid: 9305, tier: TalentTier.L4, name: "法师·蓄势", info: "法师触发类技能所需次数 -{value}", icon: "144-Cryomancy",
|
||||
bonuses: [], t_num_reduce_per_lv: 1,
|
||||
max_lv: 2, role: HRole.Mage,
|
||||
},
|
||||
9306: {
|
||||
uuid: 9306, tier: TalentTier.L4, name: "辅助·蓄势", info: "辅助触发类技能所需次数 -{value}", icon: "40-Holy",
|
||||
bonuses: [], t_num_reduce_per_lv: 1,
|
||||
max_lv: 2, role: HRole.Support,
|
||||
},
|
||||
|
||||
// ==================== 第 5 层(9401-9499,第 4 层投入 5 点解锁,概率额外触发,按职业) ====================
|
||||
9401: {
|
||||
uuid: 9401, tier: TalentTier.L5, name: "坦克·余震", info: "坦克触发类技能 +{value}% 概率额外触发 1 次", icon: "36-Guardian-Stance",
|
||||
bonuses: [], extra_trigger_chance_per_lv: 10,
|
||||
max_lv: 5, role: HRole.Tank, prereq_uuid: 9301, prereq_lv: 1,
|
||||
},
|
||||
9402: {
|
||||
uuid: 9402, tier: TalentTier.L5, name: "战士·余威", info: "战士触发类技能 +{value}% 概率额外触发 1 次", icon: "113-Rage",
|
||||
bonuses: [], extra_trigger_chance_per_lv: 10,
|
||||
max_lv: 5, role: HRole.Warrior, prereq_uuid: 9302, prereq_lv: 1,
|
||||
},
|
||||
9403: {
|
||||
uuid: 9403, tier: TalentTier.L5, name: "刺客·残影", info: "刺客触发类技能 +{value}% 概率额外触发 1 次", icon: "136-Heart-Rip",
|
||||
bonuses: [], extra_trigger_chance_per_lv: 10,
|
||||
max_lv: 5, role: HRole.Assassin, prereq_uuid: 9303, prereq_lv: 1,
|
||||
},
|
||||
9404: {
|
||||
uuid: 9404, tier: TalentTier.L5, name: "射手·连珠", info: "射手触发类技能 +{value}% 概率额外触发 1 次", icon: "74-Satisfaction",
|
||||
bonuses: [], extra_trigger_chance_per_lv: 10,
|
||||
max_lv: 5, role: HRole.Archer, prereq_uuid: 9304, prereq_lv: 1,
|
||||
},
|
||||
9405: {
|
||||
uuid: 9405, tier: TalentTier.L5, name: "法师·回响", info: "法师触发类技能 +{value}% 概率额外触发 1 次", icon: "144-Cryomancy",
|
||||
bonuses: [], extra_trigger_chance_per_lv: 10,
|
||||
max_lv: 5, role: HRole.Mage, prereq_uuid: 9305, prereq_lv: 1,
|
||||
},
|
||||
9406: {
|
||||
uuid: 9406, tier: TalentTier.L5, name: "辅助·福音", info: "辅助触发类技能 +{value}% 概率额外触发 1 次", icon: "40-Holy",
|
||||
bonuses: [], extra_trigger_chance_per_lv: 10,
|
||||
max_lv: 5, role: HRole.Support, prereq_uuid: 9306, prereq_lv: 1,
|
||||
},
|
||||
};
|
||||
|
||||
/** 天赋升级消耗:按阶位统一消耗,同一阶内每级消耗相同 */
|
||||
export const TALENT_UPGRADE_COST: Record<number, number> = {
|
||||
[TalentTier.Basic]: 1, // 基础天赋:1 点升 1 级
|
||||
[TalentTier.Specialized]: 2, // 专精天赋:2 点升 1 级
|
||||
[TalentTier.Super]: 2, // 传说天赋:2 点升 1 级
|
||||
};
|
||||
/** 天赋升级消耗:全部天赋统一 1 点/级 */
|
||||
export const TALENT_UPGRADE_COST = 1;
|
||||
|
||||
/**
|
||||
* 查询升级所需天赋点数
|
||||
* @param tier 天赋所属阶位
|
||||
* @returns 升 1 级所需点数;非法阶位返回 -1
|
||||
* @param _tier 天赋所属层位(保留参数以兼容旧调用;统一点价后不再按层区分)
|
||||
* @returns 升 1 级所需点数(恒为 1)
|
||||
*/
|
||||
export function getTalentUpgradeCost(tier: TalentTier): number {
|
||||
return TALENT_UPGRADE_COST[tier] ?? -1;
|
||||
export function getTalentUpgradeCost(_tier: TalentTier): number {
|
||||
return TALENT_UPGRADE_COST;
|
||||
}
|
||||
|
||||
/** 天赋等级上限(不含):等级从 0 起步,区间 0~29,共 30 级;每升 1 级产出 1 点天赋点 */
|
||||
@@ -308,42 +315,51 @@ export function getTalentLevelExp(curLv: number): number {
|
||||
return TALENT_LEVEL_EXP[curLv];
|
||||
}
|
||||
|
||||
/** 阶位解锁门槛:前一阶累计投入 ≥ 该点数才能升级下一阶天赋 */
|
||||
export const TALENT_TIER_UNLOCK_THRESHOLD = 10;
|
||||
/** 层位解锁门槛:前一层累计投入 ≥ 该点数才能升级下一层天赋 */
|
||||
export const TALENT_TIER_UNLOCK_THRESHOLD = 5;
|
||||
|
||||
/**
|
||||
* 计算指定阶位已投入的天赋点数(按该阶统一消耗 × 等级累加)。
|
||||
* Why: 阶位解锁按"实际消耗点数"而非"等级总和"口径——与玩家直觉(投入了多少点)
|
||||
* 计算指定层位已投入的天赋点数(统一点价 × 等级累加)。
|
||||
* Why: 层位解锁按"实际消耗点数"而非"等级总和"口径——与玩家直觉(投入了多少点)
|
||||
* 及重置退款口径(resetTalents 同表对账)保持一致。
|
||||
* @param levels 天赋等级表(key 为天赋 uuid,value 为当前等级)
|
||||
* @param tier 目标阶位
|
||||
* @returns 该阶位全部天赋已投入的点数总和
|
||||
* @param tier 目标层位
|
||||
* @returns 该层位全部天赋已投入的点数总和
|
||||
*/
|
||||
export function getTalentTierInvestedPoints(levels: Record<number, number>, tier: TalentTier): number {
|
||||
if (!levels) return 0;
|
||||
const cost = getTalentUpgradeCost(tier);
|
||||
if (cost <= 0) return 0;
|
||||
let invested = 0;
|
||||
for (const key in levels) {
|
||||
const cfg = TalentSet[key];
|
||||
if (!cfg || cfg.tier !== tier) continue;
|
||||
invested += levels[key] * cost;
|
||||
invested += levels[key] * TALENT_UPGRADE_COST;
|
||||
}
|
||||
return invested;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定阶位是否已解锁(加点入口校验用;战斗结算无需关心阶位——已投入的等级恒生效)。
|
||||
* 规则:一阶恒解锁;二阶要求一阶累计投入 ≥ TALENT_TIER_UNLOCK_THRESHOLD;三阶要求二阶累计投入达标。
|
||||
* 判断指定层位是否已解锁(加点入口校验用;战斗结算无需关心层位——已投入的等级恒生效)。
|
||||
* 规则:第 1 层恒解锁;第 N 层要求第 N-1 层累计投入 ≥ TALENT_TIER_UNLOCK_THRESHOLD。
|
||||
* @param levels 天赋等级表
|
||||
* @param tier 目标阶位
|
||||
* @param tier 目标层位
|
||||
*/
|
||||
export function isTalentTierUnlocked(levels: Record<number, number>, tier: TalentTier): boolean {
|
||||
if (tier === TalentTier.Basic) return true;
|
||||
if (tier === TalentTier.L1) return true;
|
||||
const prevTier = (tier - 1) as TalentTier;
|
||||
return getTalentTierInvestedPoints(levels, prevTier) >= TALENT_TIER_UNLOCK_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断天赋的软前置是否已满足(加点入口校验用;未满足仅限制加点,不影响已有点数的结算)。
|
||||
* @param levels 天赋等级表
|
||||
* @param cfg 天赋配置
|
||||
* @returns 无前置声明或前置天赋等级达标时返回 true
|
||||
*/
|
||||
export function isTalentPrereqMet(levels: Record<number, number>, cfg: TalentConfig): boolean {
|
||||
if (!cfg.prereq_uuid || !cfg.prereq_lv) return true;
|
||||
return (levels?.[cfg.prereq_uuid] ?? 0) >= cfg.prereq_lv;
|
||||
}
|
||||
|
||||
/** 天赋属性的卡面显示名(迷你卡空间有限,用短标签) */
|
||||
const TALENT_ATTR_LABELS: Partial<Record<Attrs, string>> = {
|
||||
[Attrs.ap]: "攻击",
|
||||
@@ -376,6 +392,15 @@ const TALENT_ATTR_SUFFIX: Partial<Record<Attrs, string>> = {
|
||||
*/
|
||||
export function getTalentDisplayText(cfg: TalentConfig, lv: number): string {
|
||||
const mult = lv > 0 ? lv : 1;
|
||||
// L4/L5 效果词条:不走 bonuses,按专属字段生成文案
|
||||
if (cfg.t_num_reduce_per_lv) {
|
||||
const text = `触发次数 -${cfg.t_num_reduce_per_lv * mult}`;
|
||||
return lv > 0 ? text : `${text}/级`;
|
||||
}
|
||||
if (cfg.extra_trigger_chance_per_lv) {
|
||||
const text = `额外触发 +${cfg.extra_trigger_chance_per_lv * mult}%`;
|
||||
return lv > 0 ? text : `${text}/级`;
|
||||
}
|
||||
const parts: string[] = [];
|
||||
for (const b of cfg.bonuses) {
|
||||
const label = TALENT_ATTR_LABELS[b.attr] ?? String(b.attr);
|
||||
@@ -388,8 +413,8 @@ export function getTalentDisplayText(cfg: TalentConfig, lv: number): string {
|
||||
|
||||
/**
|
||||
* 聚合指定职业英雄生效的全部天赋加成。
|
||||
* Why: 基础/超强全局天赋全局生效,专精与超强职业天赋按 role 过滤;
|
||||
* 阶位解锁只约束加点阶段,战斗加载时(Hero.load / applyHeroLevel)按当前等级直接取加成。
|
||||
* Why: 全局天赋全局生效,职业天赋按 role 过滤;
|
||||
* 层位解锁与软前置只约束加点阶段,战斗加载时(Hero.load / applyHeroLevel)按当前等级直接取加成。
|
||||
* @param levels 天赋等级表(key 为天赋 uuid,value 为当前等级)
|
||||
* @param role 英雄职业(职业过滤依据;无 role 时只应用全局天赋)
|
||||
* @returns 已解析的加成列表(等级已乘算进 value)
|
||||
@@ -412,7 +437,47 @@ export function getTalentBonusesForRole(levels: Record<number, number>, role?: H
|
||||
}
|
||||
|
||||
/**
|
||||
* 将天赋加成写入英雄属性模型(在 Hero.load / MissionCardComp.applyHeroLevel 的属性组装段调用)。
|
||||
* 聚合指定职业英雄的 L4 触发阈值修正量。
|
||||
* @param levels 天赋等级表
|
||||
* @param role 英雄职业
|
||||
* @returns 触发计数阈值的总减少量(SkillTriggerHelper 端再 clamp 下限 1)
|
||||
*/
|
||||
export function getTalentTriggerNumReduce(levels: Record<number, number>, role?: HRole): number {
|
||||
let total = 0;
|
||||
if (!levels) return total;
|
||||
for (const key in levels) {
|
||||
const lv = levels[key];
|
||||
if (lv <= 0) continue;
|
||||
const cfg = TalentSet[key];
|
||||
if (!cfg || !cfg.t_num_reduce_per_lv) continue;
|
||||
if (cfg.role !== undefined && cfg.role !== role) continue;
|
||||
total += cfg.t_num_reduce_per_lv * lv;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合指定职业英雄的 L5 额外触发概率(百分点)。
|
||||
* @param levels 天赋等级表
|
||||
* @param role 英雄职业
|
||||
* @returns 额外触发 1 次的概率(百分点,可超过 100 由消费端自行 clamp)
|
||||
*/
|
||||
export function getTalentExtraTriggerChance(levels: Record<number, number>, role?: HRole): number {
|
||||
let total = 0;
|
||||
if (!levels) return total;
|
||||
for (const key in levels) {
|
||||
const lv = levels[key];
|
||||
if (lv <= 0) continue;
|
||||
const cfg = TalentSet[key];
|
||||
if (!cfg || !cfg.extra_trigger_chance_per_lv) continue;
|
||||
if (cfg.role !== undefined && cfg.role !== role) continue;
|
||||
total += cfg.extra_trigger_chance_per_lv * lv;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将天赋加成写入英雄属性模型(统一由 HeroAttrsComp.applyTalent 调用)。
|
||||
*
|
||||
* 结算规则:
|
||||
* - Percent 模式:以基础快照为基数(ap→base_ap,hp_max→base_hp,defense→def_base),按百分比放大后累加;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { v3 } from "cc"
|
||||
import { v3 } from "cc"
|
||||
import { BoxSet, FacSet, FightSet } from "./GameSet"
|
||||
import { SkillOverrides, TGroup, SkillSet, FieldSkillSet, SkillConfig, SkillKind, FieldSkillConfig, FieldSkillType, mergeSkillParams } from "./SkillSet"
|
||||
import { SkillOverrides, TGroup, SkillSet, FieldSkillSet, SkillConfig, SkillKind, FieldSkillConfig, FieldSkillType, mergeSkillParams, BuffTarget } from "./SkillSet"
|
||||
import { BuffList, BuffConfig, ModOp } from "./BuffSet"
|
||||
import { Attrs } from "./HeroAttrs"
|
||||
|
||||
@@ -58,6 +58,30 @@ export const AtkSpeedSet = {
|
||||
[AtkSpeedLv.VerySlow]: { name: "极慢", cd: 1.50 },
|
||||
};
|
||||
|
||||
/**
|
||||
* 英雄大招(skills[1])CD 分级配置(5 档,与 AtkSpeedLv 五档结构对齐)。
|
||||
* 分档规则:VeryFast=单体爆发/高频压制,Normal=群体伤害/控制,VerySlow=全体增益/治疗/护盾,
|
||||
* Fast/Slow 为中间档,供同类技能内细分释放节奏。
|
||||
* Why: 大招 CD 曾 18/19 英雄统一 8s,场上大招齐发成"脉冲",玩家难以感知单次大招释放;
|
||||
* 按技能效果类型分档交错 CD(6~10s),形成持续鼓点节奏,拉开"高频小爆发 vs 稀有大回报"的感知差。
|
||||
* TODO: CD 档位与数值联动补偿(短 CD 单次数值下调、长 CD 上调)待观察实战强度后统一调整。
|
||||
*/
|
||||
export enum UltCdLv {
|
||||
VeryFast = 1, // 单体爆发/高频压制
|
||||
Fast = 2, // 频繁压制(中间档)
|
||||
Normal = 3, // 群体伤害/控制
|
||||
Slow = 4, // 中度团队增益(中间档)
|
||||
VerySlow = 5, // 全体增益/治疗/护盾
|
||||
}
|
||||
|
||||
export const UltCdSet = {
|
||||
[UltCdLv.VeryFast]: { name: "极频", cd: 6 },
|
||||
[UltCdLv.Fast]: { name: "高频", cd: 7 },
|
||||
[UltCdLv.Normal]: { name: "标准", cd: 8 },
|
||||
[UltCdLv.Slow]: { name: "低频", cd: 9 },
|
||||
[UltCdLv.VerySlow]: { name: "稀有", cd: 10 },
|
||||
};
|
||||
|
||||
export const HeroPos = {
|
||||
0: { pos: v3(-320, BoxSet.GAME_LINE, 0) },
|
||||
1: { pos: v3(0, BoxSet.GAME_LINE, 0) },
|
||||
@@ -400,8 +424,8 @@ export interface heroInfo {
|
||||
[SkillTriggerType.FStart]?: TriggerGrouped; // 战斗开始时释放的技能配置
|
||||
[SkillTriggerType.FEnd]?: TriggerGrouped; // 回合结束时释放的技能配置
|
||||
[SkillTriggerType.Field]?: LvFieldEntry[]; // 驻场光环(按 lv 升序,运行时取 ≤英雄等级 的最大档)
|
||||
[SkillTriggerType.Atking]?: TriggerGrouped; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数
|
||||
[SkillTriggerType.Atked]?: TriggerGrouped; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数
|
||||
[SkillTriggerType.Atking]?: TriggerGrouped; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数, t_chance: 触发概率(%),pity_add: 未触发时下次概率增量
|
||||
[SkillTriggerType.Atked]?: TriggerGrouped; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数, t_chance: 触发概率(%),pity_add: 未触发时下次概率增量
|
||||
dis?: number; // 攻击距离(像素)
|
||||
speed?: number; // 移动速度(像素/秒)
|
||||
dodge?: number; // 闪避率(百分点,缺省由初始化处兜底为全局默认值)
|
||||
@@ -480,6 +504,11 @@ export interface LvTriggerEntry {
|
||||
s_lv?: SkillLv; // 此档对应的技能等级(标注用,便于面板显示"技能Lv.x")
|
||||
name?: string; // 该 s_uuid 触发技能组的对外展示名(写在第 1 档,作为组名)
|
||||
t_num: number;
|
||||
/** 触发概率(百分点 0~100,缺省 100 必触发;仅 Atking/Atked 攻受击触发生效) */
|
||||
t_chance?: number;
|
||||
/** 保底叠加:判定未触发时,为该 s_uuid 累积下次概率增量(百分点)。
|
||||
* 缺省/0 = 不叠加;触发成功后清零,最终概率封顶 100 */
|
||||
pity_add?: number;
|
||||
overrides?: SkillOverrides;
|
||||
/** 此档描述模板:缺省回退该组第 1 档 desc;由 HeroSkillText 渲染合并后实时数值。
|
||||
* 显式配置后弹窗提升档(slv2+)直接展示该模板渲染结果,优先于自动 diff */
|
||||
@@ -492,6 +521,20 @@ export interface LvTriggerEntry {
|
||||
*/
|
||||
export type TriggerGrouped = Record<number /*s_uuid*/, LvTriggerEntry[]>;
|
||||
|
||||
/**
|
||||
* 运行时生效的触发条目(resolveTriggerByLv 产出):
|
||||
* 每个 s_uuid 取 ≤lv 最大档的扁平结果,供触发器/评分/文案消费。
|
||||
*/
|
||||
export interface ResolvedTriggerEntry {
|
||||
s_uuid: number;
|
||||
t_num: number;
|
||||
/** 触发概率(百分点,缺省 100 必触发;仅 Atking/Atked 生效) */
|
||||
t_chance?: number;
|
||||
/** 保底叠加:未触发时累积的下次概率增量(百分点,触发成功后清零) */
|
||||
pity_add?: number;
|
||||
overrides?: SkillOverrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* 驻场光环词条:uuid 指向 FieldSkillSet 底座(类型/图标/元数据),
|
||||
* value 为该词条实际生效数值(由配置处按档位指定,不再随 uuid 分档)。
|
||||
@@ -553,7 +596,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120}, desc: "普通攻击 造成 120% 伤害" },
|
||||
],
|
||||
},
|
||||
6301: { uuid: 6301, name: "魔法盾", lv: 1, cd: 10, ccd: 0, overrides: {"ap":30}, desc: "{target}获得生命最大值{ap}%的护盾",
|
||||
6301: { uuid: 6301, name: "魔法盾", lv: 1, cd: UltCdSet[UltCdLv.VerySlow].cd, ccd: 0, overrides: {"ap":30}, desc: "{target}获得生命最大值{ap}%的护盾",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":60}, desc: "{target}获得生命最大值{ap}%的护盾" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":120}, desc: "{target}获得生命最大值{ap}%的护盾" },
|
||||
@@ -561,40 +604,40 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
},
|
||||
atked: {
|
||||
6301: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "全体圣盾", t_num: 3, overrides: {"TGroup":null,"ap":10,"icon":"Stat_UnitSummonIncrease_01"}, desc: "全队获得本英雄生命最大值{ap}%的护盾" },
|
||||
6412: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 50, pity_add: 20, overrides: {"ap":1} },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"def":50}, name: "钢铁壁垒", icon: "46-Iron-Clad", },
|
||||
],
|
||||
info: "自身强护盾+群体小护盾,强生存",
|
||||
info: "自身强护盾+防御强化",
|
||||
},
|
||||
// 5002 易伤剑仙 — CSV导入:击退压场+易伤放大;复刻酒仙
|
||||
5002: {
|
||||
uuid: 5002, name: "易伤剑仙", path: "hk3", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: HERO_ROLE_BASE[HRole.Warrior].hp + 5, ap: HERO_ROLE_BASE[HRole.Warrior].ap + 20, def: HERO_ROLE_BASE[HRole.Warrior].def + 5,
|
||||
uuid: 5002, name: "易伤剑仙", path: "hk5", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: HERO_ROLE_BASE[HRole.Warrior].hp + 5, ap: HERO_ROLE_BASE[HRole.Warrior].ap + 20, def: HERO_ROLE_BASE[HRole.Warrior].def + 5,
|
||||
skills: {
|
||||
6004: { uuid: 6004, name: "强化普攻", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: {"ap":100}, desc: "对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120}, desc: "普通攻击造成 120% 攻击伤害" },
|
||||
],
|
||||
},
|
||||
6101: { uuid: 6101, name: "气斩", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"timed_debuff_id":7002,"icon":"Stat_AttackSpeed_02"}, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,并使目标易伤(受到伤害加深,持续{dur}秒)",
|
||||
6101: { uuid: 6101, name: "气斩", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":150,"timed_debuff_id":7002,"debuff_value":30,"debuff_chance":100,"debuff_duration":5}, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,并使目标易伤(受到伤害加深,持续{dur}秒)",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"buff_value":20} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":180,"bck":60,"buff_value":40}, desc: "斩出气刃,对{target}造成攻击力{ap}%伤害,击退{bck},并使目标易伤(持续{dur}秒)" },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"timed_debuff_id":7002,"debuff_value":70,"debuff_chance":100,"debuff_duration":5} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"timed_debuff_id":7002,"debuff_value":150,"debuff_chance":100,"debuff_duration":7} },
|
||||
],
|
||||
},
|
||||
},
|
||||
atking: {
|
||||
6004: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "风暴打击", t_num: 3, overrides: {"ap":300,"icon":"Stat_AllUnitAttack"}, desc: "立刻打击{target},造成攻击力{ap}%伤害" },
|
||||
6001: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 50, pity_add: 20, overrides: {"timed_debuff_id":7002,"debuff_value":10,"debuff_chance":100,"debuff_duration":5} },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"hp":50}, name: "不屈战意", icon: "29-Endure", },
|
||||
],
|
||||
info: "易伤+风怒,近战 dps",
|
||||
info: "易伤,近战 dps",
|
||||
},
|
||||
// 5003 眩晕暴击哥 — CSV导入:高爆发+击晕收割;复刻齐天大圣
|
||||
5003: {
|
||||
@@ -605,16 +648,18 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"stun":10}, desc: "普通攻击有{stun}%概率击晕敌人" },
|
||||
],
|
||||
},
|
||||
6104: { uuid: 6104, name: "开山斧", lv: 1, cd: 8, ccd: 0, overrides: {"ap":300,"stun":100}, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,必定击晕",
|
||||
6104: { uuid: 6104, name: "开山斧", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":250,"stun":100}, desc: "劈出开山斧,对{target}造成攻击力{ap}%伤害,必定击晕",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":450,"stun":100} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":450,"stun":100} },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":300,"stun":100} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350,"stun":100} },
|
||||
],
|
||||
},
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7005, value: 0.25, name: "大圣神威" }] },
|
||||
],
|
||||
atking: {
|
||||
6504: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 50, pity_add: 20, overrides: {"buff_value":30}, desc: "{t_chance}% 概率提升普通攻击 30%暴击率5秒" },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"hp":50}, name: "不屈战意", icon: "29-Endure", },
|
||||
],
|
||||
@@ -629,16 +674,18 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"crt":10}, desc: "普通攻击有{crt}%概率暴击" },
|
||||
],
|
||||
},
|
||||
6100: { uuid: 6100, name: "光闪", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150}, desc: "斩出光闪,对{target}造成攻击力{ap}%伤害",
|
||||
6150: { uuid: 6150, name: "剑刃风暴", lv: 1, cd: UltCdSet[UltCdLv.VeryFast].cd, ccd: 0, overrides: {"ap":150,"crt":50}, desc: "对{target}造成攻击力{ap}%伤害,有50% 概率暴击",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350} },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"crt":60} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"crt":70} },
|
||||
],
|
||||
},
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7004, value: 0.12, name: "剑刃风暴" }, { uuid: 7005, value: 0.2, name: "斩魂利刃" }] },
|
||||
],
|
||||
atking: {
|
||||
6504: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 50, pity_add: 20, overrides: {"timed_buff_id":7012,"buff_value":10,"buff_chance":100,"buff_duration":5}, desc: "{t_chance}% 概率,普通攻击获得 10% 的暴击率" },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "致命本能", icon: "36-Guardian-Stance", },
|
||||
],
|
||||
@@ -650,19 +697,21 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
skills: {
|
||||
6002: { uuid: 6002, name: "强化普攻", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0, overrides: {"ap":100}, desc: "挥刀对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"timed_debuff_id":7007,"debuff_value":30,"debuff_chance":30}, desc: "普通攻击有{debuff_chance}%概率使目标出血(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"timed_debuff_id":7007,"debuff_value":-30,"debuff_chance":50}, desc: "普通攻击有{debuff_chance}%概率使目标出血(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
6103: { uuid: 6103, name: "闪电斩", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150}, desc: "高速斩击{target},造成攻击力{ap}%伤害",
|
||||
6150: { uuid: 6150, name: "血刃风暴", lv: 1, cd: UltCdSet[UltCdLv.VeryFast].cd, ccd: 0, overrides: {"ap":200,"timed_debuff_id":7007,"debuff_value":-20,"debuff_chance":100,"debuff_duration":5}, desc: "高速斩击{target},造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250,"timed_debuff_id":7007,"debuff_value":40}, desc: "高速斩击{target},造成攻击力{ap}%,并使目标出血(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350,"timed_debuff_id":7007,"debuff_value":60}, desc: "高速斩击{target},造成攻击力{ap}%,附带 50% 概率麻痹,并使目标出血(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250,"timed_debuff_id":7007,"debuff_value":-30,"debuff_chance":100,"debuff_duration":7}, desc: "高速斩击{target},造成攻击力{ap}%,并使目标出血(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":300,"timed_debuff_id":7007,"debuff_value":-50,"debuff_chance":100,"debuff_duration":9}, desc: "高速斩击{target},造成攻击力{ap}%,附带 50% 概率麻痹,并使目标出血(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7002, value: 10, name: "攻击光环" }] },
|
||||
],
|
||||
atking: {
|
||||
6401: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 50, pity_add: 20, overrides: {"buff_value":2} },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "致命本能", icon: "36-Guardian-Stance", },
|
||||
],
|
||||
@@ -677,16 +726,18 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"frz":10}, desc: "普通攻击有{frz}%概率 冰冻敌人" },
|
||||
],
|
||||
},
|
||||
6150: { uuid: 6150, name: "万箭齐发·冰", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"frz":30,"icon":"Stat_AttackRangeIncrease_02"}, desc: "万箭齐发,对{target}造成攻击力{ap}%伤害,{frz}%概率冰冻",
|
||||
6150: { uuid: 6150, name: "寒冰箭", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":150,"frz":30,"icon":"Stat_AttackRangeIncrease_02"}, desc: "射出 6 支寒冰箭,对{target}造成攻击力{ap}%伤害,每支箭有{frz}%概率冰冻敌人",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"frz":50} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"frz":70} },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"frz":30} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"frz":30} },
|
||||
],
|
||||
},
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7006, value: 0.15 }] },
|
||||
],
|
||||
atking: {
|
||||
6401: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 50, pity_add: 20, overrides: {"buff_value":2,"TGroup": TGroup.Ally}, desc: "{t_chance}%概率,永久提升全队 2% 攻击力" },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "鹰眼锁定", icon: "27-Hawkeye", },
|
||||
],
|
||||
@@ -701,15 +752,15 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"bck_chance":20}, desc: "普通攻击有{bck_chance}%概率击退敌人" },
|
||||
],
|
||||
},
|
||||
6200: { uuid: 6200, name: "火焰喷射", lv: 1, cd: 8, ccd: 0, overrides: {"ap":250,"bck":100}, desc: "喷出烈焰风暴,对{target}造成攻击力{ap}%伤害,击退{bck}",
|
||||
6200: { uuid: 6200, name: "火焰喷射", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":250,"crt":50}, desc: "喷出烈焰风暴,对{target}造成攻击力{ap}%伤害,击退{bck}",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":350,"bck":100} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":450,"bck":100,"summon_count":2} },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":350,"crt":0} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":450,"crt":0,"summon_count":4} },
|
||||
],
|
||||
},
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7002, value: 0.15, name: "深渊威压" }] },
|
||||
{ lv: 4, uuids: [{ uuid: 7002, value: 0.15, name: "攻击光环" }] },
|
||||
],
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "奥术共鸣", icon: "100-Enchant-Magic", },
|
||||
@@ -725,18 +776,16 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120}, desc: "普通攻击造成 120% 攻击伤害" },
|
||||
],
|
||||
},
|
||||
6302: { uuid: 6302, name: "治疗", lv: 1, cd: 8, ccd: 0, overrides: {"ap":30}, desc: "为{target}回复攻击力{ap}%生命",
|
||||
6302: { uuid: 6302, name: "治疗", lv: 1, cd: UltCdSet[UltCdLv.VerySlow].cd, ccd: 0, overrides: {"ap":30,"TGroup": TGroup.Ally}, desc: "为{target}回复攻击力{ap}%生命",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":40} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":50} },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":40,"TGroup": TGroup.Ally} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":50,"TGroup": TGroup.Ally,"timed_buff_id":7024,"buff_value":20,"buff_chance":100,"buff_duration":5} },
|
||||
],
|
||||
},
|
||||
},
|
||||
atking: {
|
||||
6301: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "圣愈之盾", t_num: 3, overrides: {"ap":30}, desc: "{target}获得生命最大值{ap}%的护盾" },
|
||||
],
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7001, value: 20 }], desc: "每回合结束 休整恢复的血量值提升 20%" },
|
||||
],
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"hp":50}, name: "守护之心", icon: "29-Endure", },
|
||||
],
|
||||
@@ -744,23 +793,23 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5009 甲虫领主
|
||||
5009: {
|
||||
uuid: 5009, name: "甲虫领主", path: "hk2", fac: FacSet.HERO, rarity: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def,
|
||||
uuid: 5009, name: "甲虫领主", path: "hk3", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def,
|
||||
skills: {
|
||||
6004: { uuid: 6004, name: "普攻强化", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0,
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"proc_skill":{"s_uuid":6502,"chance":10,"overrides":{"ap":5,"TGroup": TGroup.Self}}}, desc: "普通攻击有 10% 的概率为自己释放回春(5 秒恢复生命值的{ap}%)" },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120}, desc: "普通攻击造成的伤害提升 20%" },
|
||||
],
|
||||
},
|
||||
6102: { uuid: 6102, lv: 1, cd: 8, ccd: 0, overrides: {"ap":250,"bck":30},
|
||||
6102: { uuid: 6102, lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":250,"proc_skill":{"s_uuid":6302,"chance":100,"overrides":{"TGroup": TGroup.Self,"buff_value":10}}},
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":300,"bck":50} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350,"bck":70} },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":300,"proc_skill":{"s_uuid":6302,"chance":100,"overrides":{"TGroup": TGroup.Self,"buff_value":15}}} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350,"proc_skill":{"s_uuid":6302,"chance":100,"overrides":{"TGroup": TGroup.Self,"buff_value":20}}} },
|
||||
],
|
||||
},
|
||||
},
|
||||
atked: {
|
||||
6302: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, overrides: {"ap":5}, desc: "为自己释放治疗,恢复生命值的{ap}%" },
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 30, pity_add: 20, overrides: {"ap":5}, desc: "{t_chance}%概率,恢复{ap}%生命值" },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
@@ -770,49 +819,49 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5010 火箭哥
|
||||
5010: {
|
||||
uuid: 5010, name: "火箭哥", path: "ha2", fac: FacSet.HERO, rarity: 3, lv: 1, type: HType.Mid, grow_type: HGrowType.Archer, role: HRole.Archer, hp: HERO_ROLE_BASE[HRole.Archer].hp, ap: HERO_ROLE_BASE[HRole.Archer].ap, def: HERO_ROLE_BASE[HRole.Archer].def,
|
||||
uuid: 5010, name: "火箭哥", path: "ha2", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Mid, grow_type: HGrowType.Archer, role: HRole.Archer, hp: HERO_ROLE_BASE[HRole.Archer].hp, ap: HERO_ROLE_BASE[HRole.Archer].ap, def: HERO_ROLE_BASE[HRole.Archer].def,
|
||||
skills: {
|
||||
6006: { uuid: 6006, name: "强化普攻", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0,
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":100,"bck_chance":10} },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"crt":10} },
|
||||
],
|
||||
},
|
||||
6151: { uuid: 6151, lv: 1, cd: 8, ccd: 0, overrides: {"ap":70,"summon_count":5,"bck_chance":100},
|
||||
6151: { uuid: 6151, lv: 1, cd: UltCdSet[UltCdLv.VeryFast].cd, ccd: 0, overrides: {"ap":70,"summon_count":5,"timed_debuff_id":7006,"debuff_value":-10,"debuff_chance":100,"debuff_duration":5},
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":80,"summon_count":5,"bck_chance":100} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"bck_chance":100,"ap":100,"summon_count":6} },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":80,"timed_debuff_id":7006,"debuff_value":-20,"debuff_chance":100,"debuff_duration":5} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":100,"timed_debuff_id":7006,"debuff_value":-30,"debuff_chance":100,"debuff_duration":5} },
|
||||
],
|
||||
},
|
||||
},
|
||||
atking: {
|
||||
6008: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, overrides: {"ap":120,"bck_chance":50} },
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 50, pity_add: 20, overrides: {"ap":120} },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "鹰眼锁定", icon: "27-Hawkeye", },
|
||||
],
|
||||
info: "万箭齐射+高频击退,远程压制",
|
||||
info: "暴击",
|
||||
},
|
||||
// 5011 控血大师
|
||||
5011: {
|
||||
uuid: 5011, name: "控血大师", path: "hk3", fac: FacSet.HERO, rarity: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def,
|
||||
uuid: 5011, name: "控血大师", path: "hk2", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: HERO_ROLE_BASE[HRole.Tank].hp, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def,
|
||||
skills: {
|
||||
6004: { uuid: 6004, name: "普攻强化", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0,
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"proc_skill":{"s_uuid":6502,"chance":10,"overrides":{"ap":5}}}, desc: "普攻有 10% 概率,对自己释放回春(5 秒回复5% 生命)" },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"proc_skill":{"s_uuid":6502,"chance":10,"overrides":{"ap":5,"TGroup": TGroup.Self}}}, desc: "普攻有 10% 概率,对自己释放回春(5 秒回复5% 生命)" },
|
||||
],
|
||||
},
|
||||
6517: { uuid: 6517, name: "生命爆发", lv: 1, cd: 8, ccd: 0, overrides: {"buff_value":30,"buff_duration":10}, desc: "全体友方最大生命值提升{buff_value}%,持续{dur}秒",
|
||||
6103: { uuid: 6103, name: "闪电战", lv: 1, cd: UltCdSet[UltCdLv.VerySlow].cd, ccd: 0, overrides: {"ap":200,"timed_buff_id":7029,"buff_value":30,"buff_chance":100,"buff_duration":5,"buff_target": BuffTarget.Self}, desc: "对前方敌人造成攻击力{ap}%伤害,并提升自己最大生命值{buff_value}%,持续{dur}秒",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"buff_value":40,"buff_duration":10}, desc: "全体友方最大生命值提升{buff_value}%,持续{dur}秒" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"buff_value":50,"buff_duration":15}, desc: "全体友方最大生命值提升{buff_value}%,持续{dur}秒" },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250,"timed_buff_id":7029,"buff_value":40,"buff_chance":100,"buff_duration":5,"buff_target": BuffTarget.Self} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":300,"timed_buff_id":7029,"buff_value":50,"buff_chance":100,"buff_duration":5,"buff_target": BuffTarget.Self} },
|
||||
],
|
||||
},
|
||||
},
|
||||
atked: {
|
||||
6402: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 5, overrides: {"ap":1,"TGroup": TGroup.Ally,"hit_count":3}, desc: "全队永久增加 1% 生命最大值" },
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 5, t_chance: 30, pity_add: 15, overrides: {"ap":1,"TGroup": TGroup.Ally,"hit_count":3}, desc: "全队永久增加 1% 生命最大值" },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
@@ -822,17 +871,17 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5012 疾风法师
|
||||
5012: {
|
||||
uuid: 5012, name: "疾风法师", path: "hm2", fac: FacSet.HERO, rarity: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap, def: HERO_ROLE_BASE[HRole.Mage].def,
|
||||
uuid: 5012, name: "疾风法师", path: "hm2", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap, def: HERO_ROLE_BASE[HRole.Mage].def,
|
||||
skills: {
|
||||
6009: { uuid: 6009, name: "普攻强化", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0,
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"proc_skill":{"s_uuid":6507,"chance":10,"overrides":{"buff_value":30,"TGroup": TGroup.Self}}} },
|
||||
],
|
||||
},
|
||||
6201: { uuid: 6201, name: "飓风", lv: 1, cd: 8, ccd: 0, overrides: {"ap":100,"summon_count":2,"proc_skill":{"s_uuid":6507,"chance":20,"overrides":{"buff_value":20}}}, desc: "召唤 {summon_count} 道飓风攻击前方敌人,每道飓风造成 {ap}%伤害,每道飓风释放时有<color=#3CE66E>20</color>% 概率增加全体队友<color=#3CE66E>20</color>% 攻速",
|
||||
6201: { uuid: 6201, name: "飓风", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":100,"summon_count":2,"proc_skill":{"s_uuid":6507,"chance":20,"overrides":{"buff_value":20,"TGroup": TGroup.Ally}}}, desc: "召唤 {summon_count} 道飓风攻击前方敌人,每道飓风造成 {ap}%伤害,每道飓风释放时有<color=#3CE66E>20</color>% 概率增加全体队友<color=#3CE66E>20</color>% 攻速",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":100,"summon_count":3,"proc_skill":{"s_uuid":6507,"chance":20,"overrides":{"buff_value":20}}}, desc: "召唤 {summon_count} 道飓风攻击前方敌人,每道飓风造成 {ap}%伤害,每道飓风释放时有<color=#3CE66E>20</color>% 概率增加全体队友<color=#3CE66E>20</color>% 攻速" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":100,"summon_count":4,"proc_skill":{"s_uuid":6507,"chance":20,"overrides":{"buff_value":20}}}, desc: "召唤 {summon_count} 道飓风攻击前方敌人,每道飓风造成 {ap}%伤害,每道飓风释放时有<color=#3CE66E>20</color>% 概率增加全体队友<color=#3CE66E>20</color>% 攻速" },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":100,"summon_count":3,"proc_skill":{"s_uuid":6507,"chance":25,"overrides":{"buff_value":25,"TGroup": TGroup.Ally}}}, desc: "召唤 {summon_count} 道飓风攻击前方敌人,每道飓风造成 {ap}%伤害,每道飓风释放时有<color=#3CE66E>20</color>% 概率增加全体队友<color=#3CE66E>20</color>% 攻速" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":100,"summon_count":4,"proc_skill":{"s_uuid":6507,"chance":30,"overrides":{"buff_value":30,"TGroup": TGroup.Ally}}}, desc: "召唤 {summon_count} 道飓风攻击前方敌人,每道飓风造成 {ap}%伤害,每道飓风释放时有<color=#3CE66E>20</color>% 概率增加全体队友<color=#3CE66E>20</color>% 攻速" },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -844,42 +893,42 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
],
|
||||
info: "普攻 大招加速 buff+加速光环,加速大师",
|
||||
},
|
||||
// 5013 反伤哥
|
||||
// 5013 生命哥
|
||||
5013: {
|
||||
uuid: 5013, name: "反伤哥", path: "hh2", fac: FacSet.HERO, rarity: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Support, role: HRole.Support, hp: HERO_ROLE_BASE[HRole.Support].hp, ap: HERO_ROLE_BASE[HRole.Support].ap, def: HERO_ROLE_BASE[HRole.Support].def,
|
||||
uuid: 5013, name: "生命哥", path: "hh2", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Support, role: HRole.Support, hp: HERO_ROLE_BASE[HRole.Support].hp, ap: HERO_ROLE_BASE[HRole.Support].ap, def: HERO_ROLE_BASE[HRole.Support].def,
|
||||
skills: {
|
||||
6004: { uuid: 6004, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0,
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"timed_buff_id":7029,"buff_value":10,"buff_chance":20} },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120} },
|
||||
],
|
||||
},
|
||||
6515: { uuid: 6515, name: "荆棘护盾", lv: 1, cd: 8, ccd: 0, overrides: {"buff_value":20,"TGroup": TGroup.Ally,"hit_count":3,"buff_duration":5},
|
||||
6517: { uuid: 6517, name: "生命护盾", lv: 1, cd: UltCdSet[UltCdLv.VerySlow].cd, ccd: 0, overrides: {"buff_value":20,"TGroup": TGroup.Ally,"hit_count":3,"buff_duration":5},
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"buff_value":25,"TGroup": TGroup.Ally,"hit_count":3,"buff_duration":10} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"buff_value":30,"buff_duration":10,"hit_count":3,"TGroup": TGroup.Ally} },
|
||||
],
|
||||
},
|
||||
},
|
||||
fstart: {
|
||||
6301: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 1, overrides: {"ap":20} },
|
||||
atking: {
|
||||
6414: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, t_num: 3, t_chance: 30, pity_add: 15, overrides: {"buff_value":10,"TGroup": TGroup.Ally} },
|
||||
],
|
||||
},
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"hp":50}, name: "守护之心", icon: "29-Endure", },
|
||||
],
|
||||
info: "大招全体反伤 普攻暴击 暴击反伤辅助",
|
||||
info: "大招全体生命最大值+概率全体+反伤护盾",
|
||||
},
|
||||
// 5014 闪避哥
|
||||
5014: {
|
||||
uuid: 5014, name: "闪避哥", path: "hk4", fac: FacSet.HERO, rarity: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: HERO_ROLE_BASE[HRole.Warrior].hp, ap: HERO_ROLE_BASE[HRole.Warrior].ap, def: HERO_ROLE_BASE[HRole.Warrior].def,
|
||||
uuid: 5014, name: "闪避哥", path: "hk4", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: HERO_ROLE_BASE[HRole.Warrior].hp, ap: HERO_ROLE_BASE[HRole.Warrior].ap, def: HERO_ROLE_BASE[HRole.Warrior].def,
|
||||
skills: {
|
||||
6004: { uuid: 6004, name: "普攻强化", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0,
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"timed_buff_id":7030,"buff_value":10,"buff_chance":20} },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"crt":10} },
|
||||
],
|
||||
},
|
||||
6100: { uuid: 6100, name: "寒冰斩", lv: 1, cd: 8, ccd: 0, overrides: {"ap":200,"timed_buff_id":7030,"buff_value":50},
|
||||
6100: { uuid: 6100, name: "寒冰斩", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":200,"timed_buff_id":7030,"buff_value":50},
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250,"timed_buff_id":7030,"buff_value":50} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":350,"timed_buff_id":7030,"buff_value":80} },
|
||||
@@ -892,32 +941,29 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"hp":50}, name: "不屈战意", icon: "29-Endure", },
|
||||
],
|
||||
info: "",
|
||||
info: "强闪避",
|
||||
},
|
||||
// 5015 毒箭猎手 — 毒伤持续流:普攻淬毒叠层+毒雨铺DoT+攻击光环喂养毒伤
|
||||
5015: {
|
||||
uuid: 5015, name: "毒箭猎手", path: "ha3", fac: FacSet.HERO, rarity: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Archer, role: HRole.Archer, hp: HERO_ROLE_BASE[HRole.Archer].hp + 10, ap: HERO_ROLE_BASE[HRole.Archer].ap, def: HERO_ROLE_BASE[HRole.Archer].def,
|
||||
uuid: 5015, name: "毒箭猎手", path: "ha3", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Archer, role: HRole.Archer, hp: HERO_ROLE_BASE[HRole.Archer].hp + 10, ap: HERO_ROLE_BASE[HRole.Archer].ap, def: HERO_ROLE_BASE[HRole.Archer].def,
|
||||
skills: {
|
||||
6007: { uuid: 6007, name: "淬毒箭", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0, overrides: {"ap":100}, desc: "射出箭矢,对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"timed_debuff_id":7005,"debuff_value":30,"debuff_chance":30}, desc: "普通攻击有{debuff_chance}%概率使目标中毒(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"bck_chance":10}, desc: "普通攻击有{bck_chance}%概率击退敌人" },
|
||||
],
|
||||
},
|
||||
6150: { uuid: 6150, name: "万箭齐发·毒", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"timed_debuff_id":7005,"debuff_value":30,"debuff_chance":50,"icon":"4-Apply-Poison"}, desc: "毒箭如雨,对{target}造成攻击力{ap}%伤害,{debuff_chance}%概率使目标中毒(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)",
|
||||
6150: { uuid: 6150, name: "万箭齐发·毒", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":150,"timed_debuff_id":7005,"debuff_value":-30,"debuff_chance":50,"icon":"4-Apply-Poison"}, desc: "毒箭如雨,对{target}造成攻击力{ap}%伤害,{debuff_chance}%概率使目标中毒(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"buff_value":40,"buff_chance":60} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"buff_value":60,"buff_chance":80,"hit_count":2}, desc: "毒箭如雨,对{target}造成攻击力{ap}%伤害,{buff_chance}%概率使目标中毒(每0.5秒受到{buff_value}点伤害,持续{dur}秒),每支箭可命中{hit_count}个目标" },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"timed_debuff_id":7005,"debuff_value":-60,"debuff_chance":100,"debuff_duration":5} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"timed_debuff_id":7005,"debuff_value":-100,"debuff_chance":100,"debuff_duration":7} },
|
||||
],
|
||||
},
|
||||
},
|
||||
atking: {
|
||||
6007: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "蚀骨毒箭", t_num: 3, overrides: {"ap":80,"timed_debuff_id":7005,"debuff_value":40,"debuff_chance":100}, desc: "追加射出蚀骨毒箭,对{target}造成攻击力{ap}%伤害,必定使目标中毒(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "蚀骨毒箭", t_num: 3, t_chance: 50, pity_add: 20, overrides: {"ap":80,"timed_debuff_id":7005,"debuff_value":40,"debuff_chance":100}, desc: "追加射出蚀骨毒箭,对{target}造成攻击力{ap}%伤害,必定使目标中毒(每0.5秒受到{debuff_value}点伤害,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7002, value: 0.1, name: "淬毒磨砺" }] },
|
||||
],
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "剧毒淬炼", icon: "27-Hawkeye", },
|
||||
],
|
||||
@@ -925,27 +971,22 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5016 连珠神射 — 暴击连射爆发流:VeryFast极速普攻+概率追射必暴箭+开局团队暴击爆发
|
||||
5016: {
|
||||
uuid: 5016, name: "连珠神射", path: "ha4", fac: FacSet.HERO, rarity: 4, lv: 1, type: HType.Mid, grow_type: HGrowType.Archer, role: HRole.Archer, hp: HERO_ROLE_BASE[HRole.Archer].hp, ap: HERO_ROLE_BASE[HRole.Archer].ap + 10, def: HERO_ROLE_BASE[HRole.Archer].def,
|
||||
uuid: 5016, name: "连珠神射", path: "ha4", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Mid, grow_type: HGrowType.Archer, role: HRole.Archer, hp: HERO_ROLE_BASE[HRole.Archer].hp, ap: HERO_ROLE_BASE[HRole.Archer].ap + 10, def: HERO_ROLE_BASE[HRole.Archer].def,
|
||||
skills: {
|
||||
6008: { uuid: 6008, name: "连珠箭", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VeryFast].cd, ccd: 0, overrides: {"ap":100}, desc: "极速连射,对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"crt":20,"proc_skill":{"s_uuid":6007,"chance":20,"overrides":{"crt":100}}}, desc: "普通攻击暴击率提升{crt}%,并有概率触发连射追箭(必定暴击)" },
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"proc_skill":{"s_uuid":6007,"chance":20,"overrides":{"TGroup": TGroup.Enemy,"ap":80}}}, desc: "有 20%额外射击,造成 80% 伤害" },
|
||||
],
|
||||
},
|
||||
6151: { uuid: 6151, name: "裂空箭雨", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"crt":30,"summon_count":5}, desc: "箭雨倾泻,对{target}造成攻击力{ap}%伤害,{crt}%概率暴击",
|
||||
6151: { uuid: 6151, name: "裂空箭雨", lv: 1, cd: UltCdSet[UltCdLv.VeryFast].cd, ccd: 0, overrides: {"ap":150,"crt":30,"summon_count":5}, desc: "箭雨倾泻,对{target}造成攻击力{ap}%伤害,{crt}%概率暴击",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"crt":50} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"crt":70,"summon_count":6,"hit_count":2}, desc: "箭雨倾泻,对{target}造成攻击力{ap}%伤害,{crt}%概率暴击,每支箭可命中{hit_count}个目标" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"crt":70,"summon_count":6}, desc: "箭雨倾泻,对{target}造成攻击力{ap}%伤害,{crt}%概率暴击,每支箭可命中{hit_count}个目标" },
|
||||
],
|
||||
},
|
||||
},
|
||||
fstart: {
|
||||
6504: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "猎杀时刻", t_num: 1, overrides: {"buff_value":30}, desc: "战斗开始时释放暴击爆发,全体友方暴击率提升{buff_value}%,持续{dur}秒" },
|
||||
],
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7004, value: 0.15, name: "猎手集结" }] },
|
||||
{ lv: 4, uuids: [{ uuid: 7004, value: 0.1, name: "猎手集结" }] },
|
||||
],
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "猎杀直觉", icon: "27-Hawkeye", },
|
||||
@@ -954,25 +995,20 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5017 碎甲术士 — 破甲减防法师:fstart先手/普攻概率/大招必定三通道施加破甲7008+穿透光环,团队物理增伤位
|
||||
5017: {
|
||||
uuid: 5017, name: "碎甲术士", path: "hm3", fac: FacSet.HERO, rarity: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap + 10, def: HERO_ROLE_BASE[HRole.Mage].def,
|
||||
uuid: 5017, name: "碎甲术士", path: "hm3", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp, ap: HERO_ROLE_BASE[HRole.Mage].ap + 10, def: HERO_ROLE_BASE[HRole.Mage].def,
|
||||
skills: {
|
||||
6012: { uuid: 6012, name: "蚀甲酸瓶", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0, overrides: {"ap":100}, desc: "掷出酸瓶,对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"timed_debuff_id":7008,"debuff_chance":30}, desc: "普通攻击有{debuff_chance}%概率使目标破甲(防御降低,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
6203: { uuid: 6203, name: "碎甲能量波", lv: 1, cd: 8, ccd: 0, overrides: {"ap":150,"timed_debuff_id":7008}, desc: "释放碎甲能量波,对{target}造成攻击力{ap}%伤害,并使目标破甲(防御降低,持续{dur}秒)",
|
||||
6203: { uuid: 6203, name: "碎甲能量波", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":150,"timed_debuff_id":7008,"debuff_value":-30,"debuff_chance":100,"debuff_duration":5}, desc: "释放碎甲能量波,对{target}造成攻击力{ap}%伤害,并使目标破甲(防御降低,持续{dur}秒)",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"buff_value":-70}, desc: "释放碎甲能量波,对{target}造成攻击力{ap}%伤害,并使目标破甲加深(防御大幅降低,持续{dur}秒)" },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"timed_debuff_id":7008,"debuff_value":-50,"debuff_chance":100,"debuff_duration":5} },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"timed_debuff_id":7008,"debuff_value":-70,"debuff_chance":100,"debuff_duration":7}, desc: "释放碎甲能量波,对{target}造成攻击力{ap}%伤害,并使目标破甲加深(防御大幅降低,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
},
|
||||
fstart: {
|
||||
6012: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "先手蚀甲", t_num: 1, overrides: {"ap":150,"timed_debuff_id":7008}, desc: "战斗开始时掷出蚀甲酸瓶,对{target}造成攻击力{ap}%伤害,并必定使其破甲(防御降低,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7009, value: 0.1, name: "锐矛之赐" }] },
|
||||
],
|
||||
@@ -983,62 +1019,28 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5018 雷暴领主 — 雷暴AOE爆发+麻痹控场:先手雷击+高倍率能量波
|
||||
5018: {
|
||||
uuid: 5018, name: "雷暴领主", path: "hm4", fac: FacSet.HERO, rarity: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp - 10, ap: HERO_ROLE_BASE[HRole.Mage].ap + 20, def: HERO_ROLE_BASE[HRole.Mage].def,
|
||||
uuid: 5018, name: "雷暴领主", path: "hm4", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp - 10, ap: HERO_ROLE_BASE[HRole.Mage].ap + 20, def: HERO_ROLE_BASE[HRole.Mage].def,
|
||||
skills: {
|
||||
6009: { uuid: 6009, name: "雷霆弹", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0, overrides: {"ap":100}, desc: "掷出雷霆弹,对{target}造成攻击力{ap}%伤害",
|
||||
6013: { uuid: 6013, name: "召唤闪电", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0, overrides: {"ap":100}, desc: "召唤闪电,对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"para":15}, desc: "普通攻击有{para}%概率麻痹敌人,减缓其技能冷却恢复" },
|
||||
],
|
||||
},
|
||||
6203: { uuid: 6203, name: "雷霆裂空", lv: 1, cd: 8, ccd: 0, overrides: {"ap":200,"para":40}, desc: "释放雷霆裂空波,对{target}造成攻击力{ap}%伤害,{para}%概率麻痹敌人",
|
||||
6204: { uuid: 6204, name: "雷霆裂空", lv: 1, cd: UltCdSet[UltCdLv.Normal].cd, ccd: 0, overrides: {"ap":150,"para":50}, desc: "释放多束闪电,对{target}造成攻击力{ap}%伤害,{para}%概率麻痹敌人",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":250,"para":60}, desc: "释放雷霆裂空波,对{target}造成攻击力{ap}%伤害,{para}%概率麻痹敌人" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":300,"para":80,"hit_count":3}, desc: "释放雷霆裂空波,对{target}造成攻击力{ap}%伤害,可多次命中,{para}%概率麻痹敌人" },
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":200,"para":70}, desc: "释放雷霆裂空波,对{target}造成攻击力{ap}%伤害,{para}%概率麻痹敌人" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":250,"para":100}, desc: "释放雷霆裂空波,对{target}造成攻击力{ap}%伤害,可多次命中,{para}%概率麻痹敌人" },
|
||||
],
|
||||
},
|
||||
},
|
||||
fstart: {
|
||||
6203: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "雷击先手", t_num: 1, overrides: {"ap":150,"para":30,"icon":"100-Enchant-Magic"}, desc: "战斗开始释放雷击,对{target}造成攻击力{ap}%伤害,{para}%概率麻痹敌人" },
|
||||
],
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7003, value: 10, name: "雷云领域" }] },
|
||||
{ lv: 4, uuids: [{ uuid: 7024, value: 10, name: "雷电精准" }] },
|
||||
],
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"ap":50}, name: "奥术共鸣", icon: "100-Enchant-Magic", },
|
||||
],
|
||||
info: "雷暴爆发+麻痹控场,法系主C",
|
||||
},
|
||||
// 5019 圣盾贤者 — 护盾反伤生存法:全队圣盾+荆棘反伤+受击荆棘
|
||||
5019: {
|
||||
uuid: 5019, name: "圣盾贤者", path: "hm5", fac: FacSet.HERO, rarity: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: HERO_ROLE_BASE[HRole.Mage].hp + 20, ap: HERO_ROLE_BASE[HRole.Mage].ap - 5, def: HERO_ROLE_BASE[HRole.Mage].def + 5,
|
||||
skills: {
|
||||
6011: { uuid: 6011, name: "圣光飞瓶", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0, overrides: {"ap":100}, desc: "掷出圣光飞瓶,对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"proc_skill":{"s_uuid":6516,"chance":20,"overrides":{"timed_buff_id":7028,"buff_value":10}}}, desc: "普通攻击概率凝聚荆棘护体(受击时反弹{buff_value}%所受伤害,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
6301: { uuid: 6301, name: "奥术圣壁", lv: 1, cd: 8, ccd: 0, overrides: {"TGroup": TGroup.Ally,"ap":20,"timed_buff_id":7027,"buff_value":10}, desc: "为全体友方施加生命最大值{ap}%的护盾,并附加荆棘(受击时反弹{buff_value}点伤害,持续{dur}秒)",
|
||||
lv_entries: [
|
||||
{ lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":30,"buff_value":15}, desc: "为全体友方施加生命最大值{ap}%的护盾,并附加荆棘(受击时反弹{buff_value}点伤害,持续{dur}秒)" },
|
||||
{ lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":40,"buff_value":20}, desc: "为全体友方施加生命最大值{ap}%的护盾,并附加荆棘(受击时反弹{buff_value}点伤害,持续{dur}秒)" },
|
||||
],
|
||||
},
|
||||
},
|
||||
atked: {
|
||||
6516: [
|
||||
{ lv: 4, s_lv: SkillLv.Lv1, name: "荆棘回应", t_num: 3, overrides: {"timed_buff_id":7028,"buff_value":10,"icon":"75-Shield-Stun"}, desc: "凝聚荆棘护体,受击时反弹{buff_value}%所受伤害,持续{dur}秒" },
|
||||
],
|
||||
},
|
||||
field: [
|
||||
{ lv: 4, uuids: [{ uuid: 7022, value: 8, name: "荆棘领域" }] },
|
||||
],
|
||||
bonus: [
|
||||
{ lv: 12, attrs: {"hp":50}, name: "守护之心", icon: "29-Endure", },
|
||||
],
|
||||
info: "全队护盾+荆棘反伤,生存反击法",
|
||||
},
|
||||
// @hero-setup:heroes-end
|
||||
|
||||
/*
|
||||
@@ -1118,9 +1120,9 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
};
|
||||
|
||||
export const HeroList: number[] = [
|
||||
// 编辑器导出自动同步(共 19 个)
|
||||
// 编辑器导出自动同步(共 18 个)
|
||||
5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010,
|
||||
5011, 5012, 5013, 5014, 5015, 5016, 5017, 5018, 5019,
|
||||
5011, 5012, 5013, 5014, 5015, 5016, 5017, 5018,
|
||||
];
|
||||
// @hero-setup:region-end
|
||||
|
||||
@@ -1140,48 +1142,48 @@ export interface HeroComboInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* 推荐阵容表(几何王国复刻版 5001~5009)。
|
||||
* 推荐阵容表(基于现役 5001~5018 英雄实际技能组)。
|
||||
* 设计原则:
|
||||
* 1. 铁三角结构:1 前排(坦克)+ 1 增益(光环/先手)+ 1 主C
|
||||
* 2. 乘区分散:攻击/暴击/暴伤各占独立乘区,组合优先覆盖不同乘区避免稀释
|
||||
* 3. 发动机冗余:前排挨打/死亡触发增益,叠甲骑士(5005)死亡缠绕负责献祭点火
|
||||
* 1. 铁三角结构:heroes[0] 固定为近战前排(坦克/战士),其余按站位从近到远
|
||||
* 2. 乘区分散:攻击/攻速/暴击/穿透各占独立乘区,组合优先覆盖不同乘区避免稀释
|
||||
* 3. 主题唯一:每套阵容围绕一个流派关键词(暴击/控制/续航/破甲/光环/消耗)组队
|
||||
*/
|
||||
export const HeroComboSet: Record<number, HeroComboInfo> = {
|
||||
// 8001 暴击爆发流 — 暴击率与暴伤双乘区互不稀释,成型最强
|
||||
// 8001 暴击爆发流 — 连珠神射暴击光环喂养双物理C,成型最强
|
||||
8001: {
|
||||
uuid: 8001, name: "暴击爆发流",
|
||||
heroes: [5005, 5008, 5002],
|
||||
desc: "叠甲骑士前排抗伤叠甲,刀刀暴击的暴击光环与眩晕暴击哥的暴伤光环组成双乘区,双近战暴击爆发收割。",
|
||||
heroes: [5001, 5016, 5004],
|
||||
desc: "叠甲骑士前排抗伤+受击叠防,连珠神射暴击光环覆盖全队并以极速箭雨压场,刀刀暴击近战收割残局。",
|
||||
},
|
||||
// 8002 冰雷控制流 — 冰冻+击晕双控场,敌人出不了手
|
||||
// 8002 冰雷控制流 — 击晕+冰冻+麻痹三控链,敌人出不了手
|
||||
8002: {
|
||||
uuid: 8002, name: "冰雷控制流",
|
||||
heroes: [5007, 5004, 5001],
|
||||
desc: "微醺战神前排击退压场,冻住不谢冰冻链配合紫电魔女能量波击退击晕,击晕光环叠加,全场控制。",
|
||||
heroes: [5003, 5006, 5018],
|
||||
desc: "眩晕暴击哥前排开山斧必晕压阵,冻箭哥寒冰箭冰冻链,雷暴领主雷霆麻痹迟滞技能,三控交替敌人难以出手。",
|
||||
},
|
||||
// 8003 铁桶续航流 — 双防御前排+双治疗保血,最稳
|
||||
// 8003 铁桶续航流 — 双坦克承伤+治疗保血,最稳
|
||||
8003: {
|
||||
uuid: 8003, name: "铁桶续航流",
|
||||
heroes: [5005, 5009, 5006],
|
||||
desc: "叠甲骑士与铁壳斧弟双前排叠甲叠防,光明奶爸治疗+生命光环抬全队血线,专打持久消耗战。",
|
||||
heroes: [5009, 5011, 5008],
|
||||
desc: "甲虫领主受击自疗顶前排,控血大师受击全队永久生命成长+大招生命爆发,团疗奶爸全体治疗+战后回复光环,专打持久战。",
|
||||
},
|
||||
// 8004 先手爆发流 — 入场剑气先手+能量波跟进,短回合最强
|
||||
// 8004 破甲压制流 — 破甲+减攻双减益,克制高防高攻阵容
|
||||
8004: {
|
||||
uuid: 8004, name: "先手爆发流",
|
||||
heroes: [5003, 5001, 5008],
|
||||
desc: "格挡反杀入场500%剑气先手压血线,紫电魔女能量波击退跟进,眩晕暴击哥开山斧收割残局。",
|
||||
uuid: 8004, name: "破甲压制流",
|
||||
heroes: [5001, 5017, 5010],
|
||||
desc: "碎甲术士普攻/大招双通道破甲+穿透光环放大物理伤害,火箭哥五箭弹幕必挂减攻削弱敌方输出,叠甲骑士前排兜底。",
|
||||
},
|
||||
// 8005 破甲推进流 — 易伤放大全队伤害,克制高血Boss
|
||||
// 8005 光环覆盖流 — 闪避/攻击/攻速三光环全覆盖
|
||||
8005: {
|
||||
uuid: 8005, name: "破甲推进流",
|
||||
heroes: [5007, 5006, 5002],
|
||||
desc: "微醺战神气斩易伤放大全队伤害,光明奶爸治疗保前排,刀刀暴击吃满易伤与自身暴击完成推进。",
|
||||
uuid: 8005, name: "光环覆盖流",
|
||||
heroes: [5014, 5007, 5012],
|
||||
desc: "闪避哥前排+闪避光环提升全队生存,击退火女攻击光环、疾风法师攻速光环持续放大全队输出,三光环增益拉满。",
|
||||
},
|
||||
// 8006 光环覆盖流 — 攻击/暴击/暴伤/生命光环全覆盖
|
||||
// 8006 易伤消耗流 — 易伤放大出血DoT,磨血收割
|
||||
8006: {
|
||||
uuid: 8006, name: "光环覆盖流",
|
||||
heroes: [5002, 5006, 5001],
|
||||
desc: "刀刀暴击暴击+暴伤双光环、紫电魔女攻击光环、光明奶爸生命光环,四光环全覆盖持续放大全队输出。",
|
||||
uuid: 8006, name: "易伤消耗流",
|
||||
heroes: [5002, 5005, 5008],
|
||||
desc: "易伤剑仙气斩+普攻双通道挂易伤放大全队伤害,出血贼普攻+大招叠出血磨血,团疗奶爸治疗保前排打消耗战。",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1256,9 +1258,9 @@ export const HERO_POWER_WEIGHTS = {
|
||||
* 解析触发技能在目标等级下的有效条目列表。
|
||||
* 每个 s_uuid 取 ≤lv 的最大档;返回扁平数组供评分/触发器消费。
|
||||
*/
|
||||
export function resolveTriggerByLv(grouped: TriggerGrouped | undefined, lv: number): { s_uuid: number; t_num: number; overrides?: SkillOverrides }[] {
|
||||
export function resolveTriggerByLv(grouped: TriggerGrouped | undefined, lv: number): ResolvedTriggerEntry[] {
|
||||
if (!grouped) return [];
|
||||
const result: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[] = [];
|
||||
const result: ResolvedTriggerEntry[] = [];
|
||||
for (const key in grouped) {
|
||||
const entries = grouped[key];
|
||||
if (!entries || entries.length === 0) continue;
|
||||
@@ -1266,7 +1268,7 @@ export function resolveTriggerByLv(grouped: TriggerGrouped | undefined, lv: numb
|
||||
for (const e of entries) {
|
||||
if (e.lv <= lv && (!pick || e.lv > pick.lv)) pick = e;
|
||||
}
|
||||
if (pick) result.push({ s_uuid: Number(key), t_num: pick.t_num, overrides: pick.overrides });
|
||||
if (pick) result.push({ s_uuid: Number(key), t_num: pick.t_num, t_chance: pick.t_chance, pity_add: pick.pity_add, overrides: pick.overrides });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1571,7 +1573,7 @@ export interface HeroPowerDetailed {
|
||||
|
||||
/** 计算普通触发类技能(atking/atked/fstart/dead)的详细价值 */
|
||||
function calcTriggerPowerDetailed(
|
||||
triggerConfigs: { s_uuid: number, t_num?: number, overrides?: SkillOverrides }[],
|
||||
triggerConfigs: ResolvedTriggerEntry[],
|
||||
hero: heroInfo,
|
||||
targetLv: number,
|
||||
type: SkillTriggerType,
|
||||
@@ -1612,7 +1614,9 @@ function calcTriggerPowerDetailed(
|
||||
}
|
||||
|
||||
const triggerCount = getTriggerCount(type, tNum, hero, seconds);
|
||||
const value = effectValue * triggerCount * scopeMul * weight;
|
||||
// 期望折算:按基础触发概率缩放(保底叠加提升的是方差体验,不影响长期期望)
|
||||
const chanceMul = (trig.t_chance ?? 100) / 100;
|
||||
const value = effectValue * triggerCount * scopeMul * weight * chanceMul;
|
||||
total += value;
|
||||
items.push({ type, s_uuid: trig.s_uuid, name: skill.name, t_num: tNum, value });
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* - 升级表 key 为目标等级:need = HERO_UPGRADE_NEED_FRAGMENTS[lv + 1],查不到即满级。
|
||||
*/
|
||||
import { HERO_UPGRADE_NEED_FRAGMENTS } from "./GameSet";
|
||||
import { HeroInfo, HeroList } from "./heroSet";
|
||||
|
||||
/** 英雄升级状态快照 */
|
||||
export interface HeroUpgradeState {
|
||||
@@ -75,3 +76,25 @@ export function getHeroUpgradeState(lv: number, fragments: number): HeroUpgradeS
|
||||
canUpgrade: !isMax && fragments >= (need as number),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 图鉴展示排序:可升级优先 → 现有等级降序 → 品质降序。
|
||||
* Why: 图鉴(HerosListComp)与英雄详情(HeroInfoComp)共用同一排序口径,
|
||||
* 保证图鉴中的卡片顺序与详情面板"前后切换"顺序一致。
|
||||
* @param getGrowth 查询英雄成长数据(lv / fragments)的回调,通常由 smc.getHeroGrowth 注入
|
||||
* @returns 排序后的英雄 uuid 数组(新数组,不修改 HeroList)
|
||||
*/
|
||||
export function getSortedHeroList(getGrowth: (uuid: number) => { lv: number; fragments: number }): number[] {
|
||||
return [...HeroList].sort((a, b) => {
|
||||
const ga = getGrowth(a);
|
||||
const gb = getGrowth(b);
|
||||
// 可升级的一批排在最左
|
||||
const ca = canHeroUpgrade(ga.lv, ga.fragments) ? 0 : 1;
|
||||
const cb = canHeroUpgrade(gb.lv, gb.fragments) ? 0 : 1;
|
||||
if (ca !== cb) return ca - cb;
|
||||
// 同批内:现有等级降序优先
|
||||
if (ga.lv !== gb.lv) return gb.lv - ga.lv;
|
||||
// 等级相同:品质降序(rarity 越大品质越高)
|
||||
return (HeroInfo[b]?.rarity ?? 1) - (HeroInfo[a]?.rarity ?? 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
uuid,英雄名称,简化说明,rarity,类型,形态,ap,hp,def,skill[0]_lv1,skill[0]_lv2,skill[1]_lv1,skill[1]_lv8,skill[1]_lv16,atking[0]_lv4,atked[0]_lv4,fstart[0]_lv4,dead[0]_lv4,field[0]_lv4
|
||||
5001,叠甲骑士,CSV导入:自身护盾+全体护盾,1,tank,近战,tank,tank+20,tank+10,6004 强化普攻(ap100;慢速1.20s;触发飞剑6001(100%)),ap120,6301 魔法盾(ap30),ap60,ap120,无,6301 全体圣盾(t_num3;ap10):全队获得本英雄生命最大值{ap}%的护盾,无,无,无
|
||||
5002,易伤剑仙,CSV导入:击退压场+易伤放大;复刻酒仙,1,warrior,近战,warrior+20,warrior+5,warrior+5,6004 强化普攻(ap100;慢速1.20s),ap120,6101 气斩(ap150;附加易伤7002),,ap180;bck60,6004 风暴打击(t_num3;ap300):立刻打击{target},造成攻击力{ap}%伤害,无,无,无,无
|
||||
5003,眩晕暴击哥,CSV导入:高爆发+击晕收割;复刻齐天大圣,1,warrior,近战,warrior+20,warrior+5,warrior+5,6004 强化普攻(ap100;慢速1.20s),stun10,6104 开山斧(ap300;stun100),ap450;stun100,ap450;stun100,无,无,无,无,7005 大圣神威0.25
|
||||
5004,刀刀暴击,CSV导入:暴击/暴伤双光环乘区;复刻剑圣,1,assassin,中程,assassin+30,assassin,assassin,6002 强化普攻(ap100;快速0.90s),crt10,6100 光闪(ap150),ap250,ap350,无,无,无,无,7004 剑刃风暴0.12+7005 斩魂利刃0.2
|
||||
5005,出血贼,CSV导入:入场先手+受击格挡;复刻剑豪,1,assassin,中程,assassin+30,assassin,assassin,6002 强化普攻(ap100;快速0.90s),附加30出血7007buff_chance30,6103 闪电斩(ap150),ap250;附加出血7007,ap350;para50;附加60出血7007,无,无,无,无,7018 连击光环1
|
||||
5006,冻箭哥,CSV导入:多重单体箭,带冰冻,1,archer,远程,archer+30,archer,archer,6006 强化普攻(ap100;中速1.05s),bck10,6150 万箭齐发·冰(ap150;frz30),ap200;frz50,ap250;frz70,无,无,无,无,7006 攻速光环0.15
|
||||
5007,击退火女,CSV导入:群体击退/麻痹压场;复刻深渊女王,1,mage,远程,mage+10,mage-5,mage,6010 强化普攻(ap100;慢速1.20s;bck30),bck40,6200 火焰喷射(ap250;bck100),ap350;bck100,ap450;bck100;summon2,无,无,无,无,7002 深渊威压0.15
|
||||
5008,团疗奶爸,CSV导入:全体治疗续航+自身护盾,1,support,远程,support+20,support+10,support,6011 强化普攻(ap100;中速1.05s),bck10,6302 治疗(ap30),ap40,ap50,6301 圣愈之盾(t_num3;ap30):{target}获得生命最大值{ap}%的护盾,无,无,无,无
|
||||
字段,5001,5002,5003,5004,5005,5006,5007,5008,5009,5010,5011,5012,5013,5014,5015,5016,5017,5018,5019
|
||||
英雄名称,叠甲骑士,易伤剑仙,眩晕暴击哥,刀刀暴击,出血贼,冻箭哥,击退火女,团疗奶爸,甲虫领主,火箭哥,控血大师,疾风法师,反伤哥,闪避哥,毒箭猎手,连珠神射,碎甲术士,雷暴领主,圣盾贤者
|
||||
简化说明,CSV导入:自身护盾+全体护盾,CSV导入:击退压场+易伤放大;复刻酒仙,CSV导入:高爆发+击晕收割;复刻齐天大圣,CSV导入:暴击/暴伤双光环乘区;复刻剑圣,CSV导入:入场先手+受击格挡;复刻剑豪,CSV导入:多重单体箭,带冰冻,CSV导入:群体击退/麻痹压场;复刻深渊女王,CSV导入:全体治疗续航+自身护盾,,,,,,,毒伤持续流:普攻淬毒叠层+毒雨铺DoT+攻击光环喂养毒伤,暴击连射爆发流:VeryFast极速普攻+概率追射必暴箭+开局团队暴击爆发,破甲减防法师:fstart先手/普攻概率/大招必定三通道施加破甲7008+穿透光环,团队物理增伤位,雷暴AOE爆发+麻痹控场:先手雷击+高倍率能量波,护盾反伤生存法:全队圣盾+荆棘反伤+受击荆棘
|
||||
rarity,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||
类型,tank,warrior,warrior,assassin,assassin,archer,mage,support,tank,archer,tank,mage,support,warrior,archer,archer,mage,mage,mage
|
||||
形态,近战,近战,近战,中程,中程,远程,远程,远程,近战,中程,近战,远程,远程,近战,远程,中程,远程,远程,远程
|
||||
ap,tank,warrior+20,warrior+20,assassin+30,assassin+30,archer+30,mage+10,support+20,tank,archer,tank,mage,support,warrior,archer,archer+10,mage+10,mage+20,mage-5
|
||||
hp,tank+20,warrior+5,warrior+5,assassin,assassin,archer,mage-5,support+10,tank,archer,tank,mage,support,warrior,archer+10,archer,mage,mage-10,mage+20
|
||||
def,tank+10,warrior+5,warrior+5,assassin,assassin,archer,mage,support,tank,archer,tank,mage,support,warrior,archer,archer,mage,mage,mage+5
|
||||
dodge,,,,,,,,,,,,,,,,,,,
|
||||
accuracy,,,,,,,,,,,,,,,,,,,
|
||||
skill[0]_lv1,6004 强化普攻(ap100;慢速1.20s),6004 强化普攻(ap100;慢速1.20s),6004 强化普攻(ap100;慢速1.20s),6002 强化普攻(ap100;快速0.90s),6002 强化普攻(ap100;快速0.90s),6006 强化普攻(ap100;中速1.05s),6010 强化普攻(ap100;慢速1.20s),6011 强化普攻(ap100;中速1.05s),6004 普攻强化(慢速1.20s),6006 强化普攻(快速0.90s),6004 普攻强化(中速1.05s),6009 普攻强化(中速1.05s),6004 攻击(中速1.05s),6004 普攻强化(中速1.05s),6007 淬毒箭(ap100;中速1.05s),6008 连珠箭(ap100;极速0.75s),6012 蚀甲酸瓶(ap100;中速1.05s),6009 雷霆弹(ap100;快速0.90s),6011 圣光飞瓶(ap100;中速1.05s)
|
||||
skill[0]_lv2,ap120,ap120,stun10,crt10,命中附加30出血7007debuff_chance30,frz10,,ap120,施法触发计时回血6502(10%;ap5),ap100,施法触发计时回血6502(10%;ap5),施法触发攻速爆发6507(10%),附加10生命爆发7029buff_chance20,附加10闪避爆发7030buff_chance20,命中附加30中毒7005debuff_chance30,crt20;施法触发蓝箭6007(20%),命中附加破甲7008debuff_chance30,para15,施法触发次级反伤6516(20%)
|
||||
skill[1]_lv1,6301 魔法盾(ap30),6101 气斩(ap150;命中附加易伤7002),6104 开山斧(ap300;stun100),6100 光闪(ap150),6103 闪电斩(ap150),6150 万箭齐发·冰(ap150;frz30),6200 火焰喷射(ap250;bck100),6302 治疗(ap30),6102 火焰斩(ap250;bck30),6151 万箭齐发.火(ap70;summon5),6517 生命爆发(),6201 飓风(ap100;summon2;施法触发攻速爆发6507(20%)),6515 荆棘护盾(),6100 寒冰斩(ap200;附加50闪避爆发7030),6150 万箭齐发·毒(ap150;命中附加30中毒7005debuff_chance50),6151 裂空箭雨(ap150;crt30;summon5),6203 碎甲能量波(ap150;命中附加破甲7008),6203 雷霆裂空(ap200;para40),6301 奥术圣壁(ap20;附加10荆棘7027)
|
||||
skill[1]_lv8,ap60,,ap450;stun100,ap250,ap250;命中附加40出血7007,ap200;frz50,ap350;bck100,ap40,ap300;bck50,ap80;summon5,,ap100;summon3;施法触发攻速爆发6507(20%),,ap250;附加50闪避爆发7030,ap200,ap200;crt50,ap200,ap250;para60,ap30
|
||||
skill[1]_lv16,ap120,ap180;bck60,ap450;stun100,ap350,ap350;命中附加60出血7007,ap250;frz70,ap450;bck100;summon2,ap50,ap350;bck70,ap100;summon6,,ap100;summon4;施法触发攻速爆发6507(20%),,ap350;附加80闪避爆发7030,ap250,ap250;crt70;summon6,ap250,ap300;para80,ap40
|
||||
atking[0]_lv4,无,6004 风暴打击(t_num3;ap300):立刻打击{target},造成攻击力{ap}%伤害,无,无,无,无,无,6301 圣愈之盾(t_num3;ap30):{target}获得生命最大值{ap}%的护盾,无,6008 红箭(t_num3;ap120):,无,无,无,无,6007 蚀骨毒箭(t_num3;ap80):追加射出蚀骨毒箭,对{target}造成攻击力{ap}%伤害,必定使目标中毒(每0.5秒受到{debuff_value}点伤害,持续{dur}秒),无,无,无,无
|
||||
atked[0]_lv4,6302 治疗(t_num3;概率50%;ap10):有 {t_chance}% 的概率,治疗自己 {ap}%生命最大值,无,无,无,无,无,无,无,6302 治疗(t_num3;ap5):为自己释放治疗,恢复生命值的{ap}%,无,6402 生命强化(t_num5;ap1):全队永久增加 1% 生命最大值,无,无,无,无,无,无,无,6516 荆棘回应(t_num3):凝聚荆棘护体,受击时反弹{buff_value}%所受伤害,持续{dur}秒
|
||||
fstart[0]_lv4,无,无,无,无,无,无,无,无,无,无,无,无,6301 护盾(t_num1;ap20):,无,无,6504 猎杀时刻(t_num1):战斗开始时释放暴击爆发,全体友方暴击率提升{buff_value}%,持续{dur}秒,6012 先手蚀甲(t_num1;ap150):战斗开始时掷出蚀甲酸瓶,对{target}造成攻击力{ap}%伤害,并必定使其破甲(防御降低,持续{dur}秒),6203 雷击先手(t_num1;ap150):战斗开始释放雷击,对{target}造成攻击力{ap}%伤害,{para}%概率麻痹敌人,无
|
||||
fend[0]_lv4,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无
|
||||
dead[0]_lv4,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无,无
|
||||
field[0]_lv4,无,无,7005 大圣神威0.25,7004 剑刃风暴0.12+7005 斩魂利刃0.2,7002 攻击光环10,7006 攻速光环0.15,7002 深渊威压0.15,无,无,无,无,7006 攻速光环10,无,7023 闪避光环10,7002 淬毒磨砺0.1,7004 猎手集结0.15,7009 锐矛之赐0.1,7003 雷云领域10,7022 荆棘领域8
|
||||
bonus,lv12 def+50%,lv12 hp+50%,lv12 hp+50%,lv12 ap+50%,lv12 ap+50%,lv12 ap+50%,lv12 ap+50%,lv12 hp+50%,lv12 def+50%,lv12 ap+50%,lv12 def+50%,lv12 ap+50%,lv12 hp+50%,lv12 hp+50%,lv12 ap+50%,lv12 ap+50%,lv12 ap+50%,lv12 ap+50%,lv12 hp+50%
|
||||
|
||||
|
@@ -13,7 +13,6 @@ import { Attrs } from "../common/config/HeroAttrs";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { FieldSkillType } from "../common/config/SkillSet";
|
||||
import { applyTalentBonuses } from "../common/config/TalentSet";
|
||||
/** 英雄实体:负责英雄节点创建、属性初始化、入场动画与销毁流程 */
|
||||
@ecs.register(`Hero`)
|
||||
|
||||
@@ -173,16 +172,6 @@ export class Hero extends ecs.Entity {
|
||||
model.skills[skill.uuid] = { ...skill, lv: Math.min(FightSet.HERO_SKILL_MAX_LV, Math.max(0, skill.lv + hero_lv - 2)), ccd: 0, overrides: resolveSkillOverrides(skill, hero_lv) };
|
||||
}
|
||||
|
||||
// 按当前英雄等级 resolve 触发技能/光环到运行时缓存,供 SkillTriggerHelper 消费
|
||||
model.runtime_call = resolveTriggerByLv(model.call, hero_lv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, hero_lv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, hero_lv);
|
||||
model.runtime_fend = resolveTriggerByLv(model.fend, hero_lv);
|
||||
model.runtime_atking = resolveTriggerByLv(model.atking, hero_lv);
|
||||
model.runtime_atked = resolveTriggerByLv(model.atked, hero_lv);
|
||||
model.runtime_field = resolveFieldByLv(model.field, hero_lv);
|
||||
// 复活已纳入 dead 触发组,无需单独解析 revive
|
||||
|
||||
// 应用额外属性加成(成长类型每级递增 + bonus 档位累加)
|
||||
// 成长增量:仅英雄生效(model.fac 已为 HERO),按 grow_type 递增曲线累计
|
||||
const grow = calcGrowBonus(hero, hero_lv);
|
||||
@@ -211,12 +200,23 @@ export class Hero extends ecs.Entity {
|
||||
|
||||
// lv12 职业专项已移除:单项属性强化统一由 bonus 配置(如 attrs: { ap: 50 })按等级注入
|
||||
|
||||
// 天赋加成层:全局天赋(一阶基础/三阶超强全局)全局生效,职业天赋(二阶专精/三阶超强职业)按 role 过滤(仅英雄生效,怪物跳过)
|
||||
// Why: 放在成长/bonus 之后、光环乘区之后,以基础快照为基数叠加,避免与乘区叠加成乘法膨胀
|
||||
// 天赋加成层:全局天赋(L1/L2)全局生效,职业天赋(L3-L5)按 role 过滤(仅英雄生效,怪物跳过)
|
||||
// Why: 放在成长/bonus 之后、runtime_* resolve 之前——触发修正需先写入模型,L4/L5 才能影响阈值判定;
|
||||
// 属性加成以基础快照为基数叠加,避免与光环乘区叠加成乘法膨胀
|
||||
if (model.fac === FacSet.HERO) {
|
||||
applyTalentBonuses(model, smc.getTalentLevels(), model.role);
|
||||
model.applyTalent();
|
||||
}
|
||||
|
||||
// 按当前英雄等级 resolve 触发技能/光环到运行时缓存,供 SkillTriggerHelper 消费
|
||||
model.runtime_call = resolveTriggerByLv(model.call, hero_lv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, hero_lv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, hero_lv);
|
||||
model.runtime_fend = resolveTriggerByLv(model.fend, hero_lv);
|
||||
model.runtime_atking = resolveTriggerByLv(model.atking, hero_lv);
|
||||
model.runtime_atked = resolveTriggerByLv(model.atked, hero_lv);
|
||||
model.runtime_field = resolveFieldByLv(model.field, hero_lv);
|
||||
// 复活已纳入 dead 触发组,无需单独解析 revive
|
||||
|
||||
// 缓存技能射程等派生数据,减少战斗帧内重复计算
|
||||
model.updateSkillDistanceCache();
|
||||
|
||||
|
||||
@@ -80,9 +80,15 @@ export default class HeroAnmComp extends Component {
|
||||
if (this.maxPlaying) return
|
||||
this.anmcon.play(`atk0`)
|
||||
}
|
||||
max() {
|
||||
/**
|
||||
* 播放大招动画。
|
||||
* Why 带 name:技能表 act 直接配置 clip 名(战士技 "max0"、法系技 "max1"),
|
||||
* 固定播 max0 会导致法系大招动画永远播不出。
|
||||
* @param name 大招 clip 名("max0"/"max1"),maxPlaying guard 已同时覆盖两者
|
||||
*/
|
||||
max(name: string = "max0") {
|
||||
if (this.maxPlaying) return
|
||||
this.anmcon.play("max0")
|
||||
this.anmcon.play(name)
|
||||
}
|
||||
play(anm: string = "") {
|
||||
if (anm === "") return
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvBonusAttrsEntry, FieldEntry, HRole } from "../common/config/heroSet";
|
||||
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType, TriggerGrouped, LvFieldEntry, LvBonusAttrsEntry, FieldEntry, HRole, ResolvedTriggerEntry } from "../common/config/heroSet";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { Attrs } from "../common/config/HeroAttrs";
|
||||
import { applyTalentBonuses, getTalentExtraTriggerChance, getTalentTriggerNumReduce } from "../common/config/TalentSet";
|
||||
import { FieldSkillHelper } from "./FieldSkillHelper";
|
||||
import { BuffComp } from "./BuffComp";
|
||||
import { BuffList, ModOp } from "../common/config/BuffSet";
|
||||
@@ -63,20 +64,26 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
|
||||
// ==================== 运行时解析缓存(按当前等级 resolve 后的有效列表) ====================
|
||||
/** 当前等级下生效的攻击触发条目(供 SkillTriggerHelper 消费) */
|
||||
runtime_atking?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_atking?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的受击触发条目 */
|
||||
runtime_atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_atked?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的召唤触发条目 */
|
||||
runtime_call?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_call?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的死亡触发条目 */
|
||||
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_dead?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的战前触发条目 */
|
||||
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_fstart?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的回合结束触发条目 */
|
||||
runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
runtime_fend?: ResolvedTriggerEntry[];
|
||||
/** 当前等级下生效的驻场光环词条列表 */
|
||||
runtime_field?: FieldEntry[];
|
||||
|
||||
// ==================== 触发概率保底缓存(key: s_uuid, value: 已累积的概率增量百分点) ====================
|
||||
/** 攻击触发未命中时累积的下次概率增量(pity_add 驱动,触发成功后清零) */
|
||||
pity_atking: Record<number, number> = {};
|
||||
/** 受击触发未命中时累积的下次概率增量(pity_add 驱动,触发成功后清零) */
|
||||
pity_atked: Record<number, number> = {};
|
||||
|
||||
// ==================== 特殊属性 ====================
|
||||
critical: number = 0; // 暴击率
|
||||
critical_res: number = 0; // 暴击抗性
|
||||
@@ -101,6 +108,10 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
thorns_dmg: number = 0; // 反伤伤害(固定值,受到攻击时对攻击者造成等量伤害)
|
||||
thorns_rate: number = 0; // 反伤比例(百分点;反伤值 = 实际受到伤害 * thorns_rate / 100 + thorns_dmg)
|
||||
|
||||
// ==================== 天赋触发修正(L4/L5 写入,SkillTriggerHelper 消费) ====================
|
||||
talent_t_num_reduce: number = 0; // L4:触发类技能计数阈值减少量(下限 1 由消费端 clamp)
|
||||
talent_extra_trigger_chance: number = 0; // L5:触发类技能额外触发 1 次的概率(百分点)
|
||||
|
||||
revived_count: number = 0; // 已复活次数(从 dead 触发配置的 revive_count 限制)
|
||||
death_count: number = 0; // 本回合死亡次数(用于 dead 触发统计)
|
||||
invincible_time: number = 0;// 无敌时间
|
||||
@@ -154,6 +165,20 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
*/
|
||||
initAttrs() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 天赋统一应用入口:写入属性加成(L1-L3)与触发修正(L4/L5)。
|
||||
* Why: 收敛到属性组件单点调用,Hero.load 与局内升级重算(applyHeroLevel)不再各自 import TalentSet;
|
||||
* 触发修正必须在 runtime_* 触发缓存 resolve 之前写入,才能影响 SkillTriggerHelper 的阈值判定。
|
||||
* 调用前请确保触发修正字段已归零(升级重算路径由调用方归零,保证幂等)。
|
||||
*/
|
||||
applyTalent(): void {
|
||||
if (this.fac !== FacSet.HERO) return;
|
||||
const levels = smc.getTalentLevels();
|
||||
this.talent_t_num_reduce = getTalentTriggerNumReduce(levels, this.role);
|
||||
this.talent_extra_trigger_chance = getTalentExtraTriggerChance(levels, this.role);
|
||||
applyTalentBonuses(this, levels, this.role);
|
||||
}
|
||||
/*******************基础属性管理********************/
|
||||
|
||||
add_hp(value: number) {
|
||||
@@ -740,6 +765,9 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.runtime_fstart = undefined;
|
||||
this.runtime_fend = undefined;
|
||||
this.runtime_field = undefined;
|
||||
// 清空触发概率保底叠加缓存
|
||||
this.pity_atking = {};
|
||||
this.pity_atked = {};
|
||||
this.critical = 0;
|
||||
this.critical_res = 0;
|
||||
this.dodge = 0;
|
||||
@@ -765,6 +793,8 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.buff_power = 0;
|
||||
this.thorns_dmg = 0;
|
||||
this.thorns_rate = 0;
|
||||
this.talent_t_num_reduce = 0;
|
||||
this.talent_extra_trigger_chance = 0;
|
||||
this.boom = false;
|
||||
|
||||
// 重置技能距离缓存
|
||||
|
||||
@@ -48,14 +48,16 @@ export class HeroSpine extends Component {
|
||||
atk() {
|
||||
this.anm.atk()
|
||||
}
|
||||
max(){
|
||||
this.anm.max()
|
||||
max(name: string = "max0"){
|
||||
this.anm.max(name)
|
||||
}
|
||||
play(name:string){
|
||||
// 技能表 act 配置的是 clip 名(max0/max1),不能用全等匹配 "max"
|
||||
if(name.startsWith("max")){
|
||||
this.max(name)
|
||||
return
|
||||
}
|
||||
switch(name){
|
||||
case "max":
|
||||
this.max()
|
||||
break
|
||||
case "atk":
|
||||
this.atk()
|
||||
break
|
||||
@@ -65,7 +67,7 @@ export class HeroSpine extends Component {
|
||||
case "move":
|
||||
this.move()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
do_atked(){
|
||||
this.anm.atked()
|
||||
|
||||
@@ -487,11 +487,14 @@ export class HeroViewComp extends CCComp {
|
||||
playSkillAnm(act: string = "") {
|
||||
mLogger.log(this.debugMode, 'HeroViewComp', '[heroview] act' + act,)
|
||||
if (act === "") return;
|
||||
// 技能表大招 act 配置为 clip 名("max0"/"max1"),必须前缀匹配,
|
||||
// 全等匹配 "max" 会落空导致大招动画不播、isCastingMax 不置位
|
||||
if (act.startsWith("max")) {
|
||||
this.isCastingMax = true;
|
||||
this.as.max(act)
|
||||
return
|
||||
}
|
||||
switch (act) {
|
||||
case "max":
|
||||
this.isCastingMax = true;
|
||||
this.as.max()
|
||||
break
|
||||
case "atk":
|
||||
this.as.atk()
|
||||
break
|
||||
|
||||
@@ -236,6 +236,21 @@ export class Monster extends ecs.Entity {
|
||||
if (testSkills.fstart !== undefined) model.fstart = testSkills.fstart;
|
||||
if (testSkills.call !== undefined) model.call = testSkills.call;
|
||||
}
|
||||
// Boss 大招注入:在保留普攻的基础上追加(多个大招共存,由 SCastSystem 按 CD 轮转施放,
|
||||
// 含镜头聚焦 + 技能名横幅表现,与英雄大招同一条施法链路)
|
||||
const ults = (this as any)._testSkills?.ults;
|
||||
if (ults && ults.length > 0) {
|
||||
for (const ult of ults) {
|
||||
if (!ult || !ult.s_uuid) continue;
|
||||
model.skills[ult.s_uuid] = {
|
||||
uuid: ult.s_uuid,
|
||||
cd: ult.cd ?? 8,
|
||||
lv: 1,
|
||||
ccd: 0,
|
||||
overrides: ult.overrides
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 按怪物等级 resolve 触发技能到运行时缓存(怪物无 lv 成长,恒用 mon_lv)
|
||||
model.runtime_call = resolveTriggerByLv(model.call, mon_lv);
|
||||
|
||||
@@ -56,7 +56,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
private readonly renderSortInterval = 0.05;
|
||||
private lastRenderSortAt = 0;
|
||||
private heroViewMatcher: ecs.IMatcher | null = null;
|
||||
private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number; laneScore: number; isCastingMax: boolean }[] = [];
|
||||
private readonly renderEntries: { node: Node; spawnOrder: number; eid: number; isCastingMax: boolean }[] = [];
|
||||
private renderEntryCount = 0;
|
||||
|
||||
// ======================== 英雄编队排队 ========================
|
||||
@@ -431,8 +431,8 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
const monRoot = layerNode.getChildByName("MON");
|
||||
const bossRoot = layerNode.getChildByName("BOSS");
|
||||
|
||||
/** 分层重排同层节点:各阵营层内按 Y 轴路次 -> 前后位置 -> 出生序 -> eid 排序
|
||||
* (Boss 已有独立 BOSS 层固定在最底,无需 bossPriority 参与排序) */
|
||||
/** 分层重排同层节点:层内层级只由「是否释放大招」决定,出生序/eid 仅保证排序稳定
|
||||
* (Boss 已有独立 BOSS 层固定在最底,天然在最下层) */
|
||||
this.renderEntryCount = 0;
|
||||
ecs.query(this.getHeroViewMatcher()).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
@@ -456,20 +456,13 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
const move = e.get(MoveComp);
|
||||
const spawnOrder = move?.spawnOrder ?? 0;
|
||||
|
||||
// 按 Y 轴计算渲染层级权重(Y 越小,说明越靠近屏幕下方,应该渲染在越前面)
|
||||
const laneScore = -actorView.node.position.y;
|
||||
// X 轴权重:站在前排的(交战处)优先渲染
|
||||
const frontScore = attrs.fac === FacSet.HERO ? actorView.node.position.x : -actorView.node.position.x;
|
||||
|
||||
const entryIndex = this.renderEntryCount;
|
||||
let entry = this.renderEntries[entryIndex];
|
||||
if (!entry) {
|
||||
entry = { node: actorView.node, bossPriority: 0, frontScore: 0, spawnOrder: 0, eid: 0, laneScore: 0, isCastingMax: false };
|
||||
entry = { node: actorView.node, spawnOrder: 0, eid: 0, isCastingMax: false };
|
||||
this.renderEntries.push(entry);
|
||||
}
|
||||
entry.node = actorView.node;
|
||||
entry.laneScore = laneScore;
|
||||
entry.frontScore = frontScore;
|
||||
entry.spawnOrder = spawnOrder;
|
||||
entry.eid = e.eid;
|
||||
entry.isCastingMax = actorView.isCastingMax;
|
||||
@@ -478,11 +471,9 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
this.renderEntries.length = this.renderEntryCount;
|
||||
|
||||
this.renderEntries.sort((a, b) => {
|
||||
// 正在播放大招的英雄排在最后,siblingIndex 越大渲染层级越靠上
|
||||
// 层级只受大招影响:正在播放大招的单位排在最后,siblingIndex 越大渲染层级越靠上
|
||||
if (a.isCastingMax !== b.isCastingMax) return a.isCastingMax ? 1 : -1;
|
||||
// Y轴靠下的(laneScore 较大)应该在后面,从而渲染在上面
|
||||
if (Math.abs(a.laneScore - b.laneScore) > 10) return a.laneScore - b.laneScore;
|
||||
if (a.frontScore !== b.frontScore) return a.frontScore - b.frontScore;
|
||||
// 与位置无关的稳定排序:出生序 -> eid
|
||||
if (a.spawnOrder !== b.spawnOrder) return a.spawnOrder - b.spawnOrder;
|
||||
return a.eid - b.eid;
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { Vec3, Prefab, instantiate, tween, Node } from "cc";
|
||||
import { Vec3, Prefab, instantiate, tween, Node, UITransform } from "cc";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { DTType, RType, SkillConfig, SkillKind, SkillSet, TGroup, SkillOverrides, mergeSkillParams } from "../common/config/SkillSet";
|
||||
import { DTType, RType, SkillConfig, SkillKind, SkillSet, SummonTMode, TGroup, SkillOverrides, mergeSkillParams } from "../common/config/SkillSet";
|
||||
import { Skill } from "../skill/Skill";
|
||||
import { SkillView } from "../skill/SkillView";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
@@ -20,6 +20,16 @@ import { BuffList } from "../common/config/BuffSet";
|
||||
import { CameraFX } from "../common/CameraFX";
|
||||
import { ScreenShake } from "../common/ScreenShake";
|
||||
|
||||
/** 召唤参数:数量 / XY 步进偏移 / 生成间隔 / 目标选择模式,均由技能 prefab 上的 SkillView 配置 */
|
||||
export interface SummonParams {
|
||||
count: number;
|
||||
x_offset: number;
|
||||
y_offset: number;
|
||||
interval: number;
|
||||
/** 多次施放的目标选择模式(同一敌人/随机敌人/避免相同敌人) */
|
||||
target_mode: SummonTMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* ==================== 自动施法系统 ====================
|
||||
*
|
||||
@@ -93,9 +103,10 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
targetEids = this.collectFriendlyTargetEids(FacSet.HERO, undefined, true); // 获取所有英雄阵营的目标
|
||||
}
|
||||
|
||||
const summonCount = this.resolveSummonCount(config);
|
||||
const cNum = summonCount > 1 ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
|
||||
const castTimes = Math.max(summonCount, 1 + cNum);
|
||||
const summon = this.resolveSummonParams(config);
|
||||
const cNum = summon.count > 1 ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
|
||||
const castTimes = Math.max(summon.count, 1 + cNum);
|
||||
const castInterval = this.resolveCastInterval(summon);
|
||||
|
||||
// 构造一个模拟的 HeroAttrsComp 用于数值计算,只包含基础卡牌伤害计算所需的属性
|
||||
const mockAttrs = new HeroAttrsComp();
|
||||
@@ -128,12 +139,12 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
mockAttrs.type = HType.Long; // 假定为远程,拥有较长索敌范围
|
||||
mockAttrs.dis = 2000; // 给予全屏以上的索敌范围
|
||||
|
||||
// 伪造一个 view 供找敌逻辑使用,位置为 spawnPos(random/unique 模式实时索敌也复用)
|
||||
const mockView = {
|
||||
node: { position: spawnPos }
|
||||
} as any;
|
||||
let targetPos: Vec3 | null = null;
|
||||
if (!isFriendly) {
|
||||
// 伪造一个 view 供找敌逻辑使用,位置为 spawnPos
|
||||
const mockView = {
|
||||
node: { position: spawnPos }
|
||||
} as any;
|
||||
// 获取全屏索敌范围
|
||||
const maxRange = this.resolveMaxCastRange(mockAttrs, mockAttrs.type as HType);
|
||||
const target = this.findNearestEnemyInRange(mockAttrs, mockView, maxRange);
|
||||
@@ -148,8 +159,10 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "SCastSystem", `[卡牌施法] 释放技能「${config.name}」(s_uuid:${s_uuid}) rarity:${rarity} castTimes:${castTimes} targetPos:${targetPos ? `(${targetPos.x.toFixed(0)},${targetPos.y.toFixed(0)})` : "none"} isFriendly:${isFriendly}`);
|
||||
// unique 模式去重记录:本次施法序列内已锁定过的敌人 eid
|
||||
const usedTargetEids = new Set<number>();
|
||||
for (let i = 0; i < castTimes; i++) {
|
||||
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*SUMMON_INTERVAL(卡牌无施法主体,用 setTimeout 并在回调内做效期校验)
|
||||
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*castInterval(召唤间隔由 prefab 配置)
|
||||
const exec = () => {
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
if (isFriendly) {
|
||||
@@ -158,14 +171,14 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
if (friendlyTargets.length === 0) return;
|
||||
this.applyFriendlySkillEffects(s_uuid, rarity, config, null as any, mockAttrs, friendlyTargets, spawnPos);
|
||||
} else {
|
||||
const enemyTargetPos = this.resolveRepeatCastTargetPos(targetPos, i);
|
||||
this.createSkillEntityForCard(s_uuid, rarity, mockAttrs, spawnPos, enemyTargetPos, i, overrides);
|
||||
const enemyTargetPos = this.resolveSummonCastTargetPos(config, mockAttrs, mockView, targetPos, i, summon, usedTargetEids);
|
||||
this.createSkillEntityForCard(s_uuid, rarity, mockAttrs, spawnPos, enemyTargetPos, i, overrides, summon);
|
||||
}
|
||||
};
|
||||
if (i === 0) {
|
||||
exec();
|
||||
} else {
|
||||
setTimeout(exec, i * FightSet.SUMMON_INTERVAL * 1000);
|
||||
setTimeout(exec, i * castInterval * 1000);
|
||||
}
|
||||
}
|
||||
// 施法后概率触发额外技能(proc_skill,原命中触发改为出手即判定;卡牌技能无视 CD、无施法主体)
|
||||
@@ -173,7 +186,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
|
||||
/** 专用于卡牌施放的技能实体生成 */
|
||||
private createSkillEntityForCard(s_uuid: number, skillLv: number, mockAttrs: HeroAttrsComp, startPos: Vec3, targetPos: Vec3 | null, castIndex: number = 0, overrides?: SkillOverrides) {
|
||||
private createSkillEntityForCard(s_uuid: number, skillLv: number, mockAttrs: HeroAttrsComp, startPos: Vec3, targetPos: Vec3 | null, castIndex: number = 0, overrides?: SkillOverrides, summon?: SummonParams) {
|
||||
const scene = smc.map.MapView.scene;
|
||||
const parent = scene.entityLayer?.node?.getChildByName("SKILL");
|
||||
if (!parent || !targetPos) {
|
||||
@@ -182,7 +195,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
|
||||
const skill = ecs.getEntity<Skill>(Skill);
|
||||
const actualStartPos = this.resolveRepeatCastStartPos(startPos, castIndex);
|
||||
const actualStartPos = this.resolveRepeatCastStartPos(startPos, castIndex, summon);
|
||||
|
||||
// 伪造一个简单的 heroView 供 Skill 初始化使用,只包含方向信息
|
||||
const mockView = {
|
||||
@@ -199,31 +212,51 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
/** 查询缓存:避免每帧重复创建 matcher */
|
||||
private heroMatcher: ecs.IMatcher | null = null;
|
||||
|
||||
/** 技能 prefab 召唤数量缓存:sp_name -> summon_count */
|
||||
private summonCountCache: Map<string, number> = new Map();
|
||||
/** 技能 prefab 召唤参数缓存:sp_name -> 召唤参数(数量/XY偏移/间隔) */
|
||||
private summonParamsCache: Map<string, SummonParams> = new Map();
|
||||
|
||||
/**
|
||||
* 解析单次施法的召唤数量。
|
||||
* 优先级:合并后配置上的 summon_count(heroSet overrides 可覆写) > prefab 上 SkillView.summon_count。
|
||||
* prefab 回退路径仅使用已缓存的资源(oops.res.get 同步命中),未加载时回退 1(单体),不引入异步等待。
|
||||
* 解析单次施法的召唤参数。
|
||||
* 数量优先级:合并后配置上的 summon_count(heroSet overrides 可覆写) > prefab 上 SkillView.summon_count;
|
||||
* XY 偏移、间隔与目标选择模式仅由 prefab 配置。
|
||||
*/
|
||||
private resolveSummonCount(config: SkillConfig): number {
|
||||
if (typeof config.summon_count === "number") {
|
||||
return Math.max(1, Math.floor(config.summon_count));
|
||||
}
|
||||
const cached = this.summonCountCache.get(config.sp_name);
|
||||
if (cached !== undefined) return cached;
|
||||
const path = `game/skill/atk/${config.sp_name}`;
|
||||
let count = 1;
|
||||
private resolveSummonParams(config: SkillConfig): SummonParams {
|
||||
const fromPrefab = this.readSummonParamsFromPrefab(config.sp_name);
|
||||
const count = typeof config.summon_count === "number"
|
||||
? Math.max(1, Math.floor(config.summon_count))
|
||||
: fromPrefab.count;
|
||||
return { count, x_offset: fromPrefab.x_offset, y_offset: fromPrefab.y_offset, interval: fromPrefab.interval, target_mode: fromPrefab.target_mode };
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 prefab 上 SkillView 的召唤参数(带缓存)。
|
||||
* prefab 回退路径仅使用已缓存的资源(oops.res.get 同步命中),未加载时回退默认值(单体、无偏移),不引入异步等待。
|
||||
*/
|
||||
private readSummonParamsFromPrefab(sp_name: string): SummonParams {
|
||||
const cached = this.summonParamsCache.get(sp_name);
|
||||
if (cached) return cached;
|
||||
const path = `game/skill/atk/${sp_name}`;
|
||||
let params: SummonParams = { count: 1, x_offset: 0, y_offset: 0, interval: 0, target_mode: SummonTMode.same };
|
||||
const prefab = oops.res.get(path, Prefab) as Prefab | null;
|
||||
if (prefab) {
|
||||
const view = prefab.data?.getComponent(SkillView);
|
||||
if (view) {
|
||||
count = Math.max(1, Math.floor(view.summon_count));
|
||||
params = {
|
||||
count: Math.max(1, Math.floor(view.summon_count)),
|
||||
x_offset: view.summon_x_offset,
|
||||
y_offset: view.summon_y_offset,
|
||||
interval: view.summon_interval,
|
||||
target_mode: view.summon_target_mode ?? SummonTMode.same,
|
||||
};
|
||||
}
|
||||
this.summonCountCache.set(config.sp_name, count);
|
||||
this.summonParamsCache.set(sp_name, params);
|
||||
}
|
||||
return count;
|
||||
return params;
|
||||
}
|
||||
|
||||
/** 解析错峰施法间隔:召唤技能优先 prefab 配置(>0),其余场景回退全局 SUMMON_INTERVAL */
|
||||
private resolveCastInterval(summon: SummonParams): number {
|
||||
return summon.count > 1 && summon.interval > 0 ? summon.interval : FightSet.SUMMON_INTERVAL;
|
||||
}
|
||||
|
||||
/** 获取英雄查询条件(包含属性与视图组件) */
|
||||
@@ -317,18 +350,21 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
}
|
||||
|
||||
const summonCount = this.resolveSummonCount(config);
|
||||
const cNum = summonCount > 1 ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
|
||||
const castTimes = Math.max(summonCount, 1 + cNum);
|
||||
const summon = this.resolveSummonParams(config);
|
||||
const cNum = summon.count > 1 ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
|
||||
const castTimes = Math.max(summon.count, 1 + cNum);
|
||||
const castInterval = this.resolveCastInterval(summon);
|
||||
let val = ""
|
||||
if (castTimes > 1) {
|
||||
val = "*" + castTimes.toString()
|
||||
}
|
||||
heroView.skill_name(val, s_uuid)
|
||||
mLogger.log(this.debugMode, "SCastSystem", `[触发施法] ${heroAttrs.hero_name}(uuid:${heroAttrs.hero_uuid}) 触发类型:${triggerType ?? "none"} 释放技能「${config.name}」(s_uuid:${s_uuid}) lv:${skillLv} ${isFriendly ? "友方" : "对敌"} castTimes:${castTimes}`);
|
||||
// unique 模式去重记录:本次施法序列内已锁定过的敌人 eid
|
||||
const usedTargetEids = new Set<number>();
|
||||
for (let i = 0; i < castTimes; i++) {
|
||||
if (!heroView.node || !heroView.node.isValid) return;
|
||||
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*SUMMON_INTERVAL
|
||||
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*castInterval(召唤间隔由 prefab 配置)
|
||||
heroView.scheduleOnce(() => {
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
|
||||
@@ -338,9 +374,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null);
|
||||
return;
|
||||
}
|
||||
const enemyTargetPos = this.resolveRepeatCastTargetPos(targetPos, i);
|
||||
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides);
|
||||
}, i * FightSet.SUMMON_INTERVAL);
|
||||
const enemyTargetPos = this.resolveSummonCastTargetPos(config, heroAttrs, heroView, targetPos, i, summon, usedTargetEids);
|
||||
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides, summon);
|
||||
}, i * castInterval);
|
||||
}
|
||||
// 施法后概率触发额外技能(proc_skill,原命中触发改为出手即判定,无视 CD)
|
||||
this.tryProcSkill(config, heroAttrs, heroView, triggerType);
|
||||
@@ -399,10 +435,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
|
||||
config = mergeSkillParams(config, overrides);
|
||||
|
||||
const summonCount = this.resolveSummonCount(config);
|
||||
const isSummonSkill = summonCount > 1;
|
||||
const summon = this.resolveSummonParams(config);
|
||||
const isSummonSkill = summon.count > 1;
|
||||
const cNum = isSummonSkill ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
|
||||
const castTimes = Math.max(summonCount, 1 + cNum);
|
||||
const castTimes = Math.max(summon.count, 1 + cNum);
|
||||
const castInterval = this.resolveCastInterval(summon);
|
||||
|
||||
mLogger.log(this.debugMode, "SCastSystem", `[施法] ${heroAttrs.hero_name}(uuid:${heroAttrs.hero_uuid}) 释放技能「${config.name}」(s_uuid:${s_uuid}) lv:${skillLv} ${castPlan.isFriendly ? "友方" : "对敌"} castTimes:${castTimes}`);
|
||||
|
||||
@@ -439,12 +476,14 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
// 注意:这里仍然是基于时间的延迟,受帧率波动影响。
|
||||
// 若需精确同步,建议在动画中添加帧事件并在 HeroViewComp 中监听。
|
||||
const delay = config.ready > 0 ? config.ready : FightSet.SKILL_CAST_DELAY;
|
||||
// unique 模式去重记录:本次施法序列内已锁定过的敌人 eid
|
||||
const usedTargetEids = new Set<number>();
|
||||
|
||||
heroView.scheduleOnce(() => {
|
||||
if (!smc.mission.play || smc.mission.pause || !smc.mission.in_fight) return;
|
||||
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
|
||||
for (let i = 0; i < castTimes; i++) {
|
||||
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*SUMMON_INTERVAL
|
||||
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*castInterval(召唤间隔由 prefab 配置)
|
||||
heroView.scheduleOnce(() => {
|
||||
if (!smc.mission.play || smc.mission.pause || !smc.mission.in_fight) return;
|
||||
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
|
||||
@@ -454,9 +493,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null);
|
||||
return;
|
||||
}
|
||||
const enemyTargetPos = this.resolveRepeatCastTargetPos(castPlan.targetPos, i);
|
||||
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides);
|
||||
}, i * FightSet.SUMMON_INTERVAL);
|
||||
const enemyTargetPos = this.resolveSummonCastTargetPos(config, heroAttrs, heroView, castPlan.targetPos, i, summon, usedTargetEids);
|
||||
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides, summon);
|
||||
}, i * castInterval);
|
||||
}
|
||||
}, delay);
|
||||
|
||||
@@ -497,14 +536,91 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
}
|
||||
|
||||
/** 多发/召唤的纵向扇形偏移档位(像素),索引按 中/上/下/上上/下下... 循环 */
|
||||
/** 多发的纵向扇形偏移档位(像素),索引按 中/上/下/上上/下下... 循环;召唤技能偏移由 prefab 配置 */
|
||||
private static readonly REPEAT_Y_OFFSETS: number[] = [0, 15, -15, 30, -30, 45, -45, 60, -60];
|
||||
|
||||
private resolveRepeatCastTargetPos(targetPos: Vec3 | null, castIndex: number): Vec3 | null {
|
||||
private resolveRepeatCastTargetPos(targetPos: Vec3 | null, castIndex: number, summon?: SummonParams): Vec3 | null {
|
||||
if (!targetPos) return null;
|
||||
const offset = SCastSystem.REPEAT_Y_OFFSETS[castIndex % SCastSystem.REPEAT_Y_OFFSETS.length];
|
||||
if (offset === 0) return targetPos;
|
||||
return new Vec3(targetPos.x, targetPos.y + offset, targetPos.z);
|
||||
const offset = this.resolveCastOffset(castIndex, summon);
|
||||
if (offset.x === 0 && offset.y === 0) return targetPos;
|
||||
return new Vec3(targetPos.x + offset.x, targetPos.y + offset.y, targetPos.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析第 castIndex 次施放的对敌目标点(召唤目标选择模式入口)。
|
||||
* - same(默认):沿用施法时锁定的目标点并按召唤偏移对称铺开(原行为);
|
||||
* - random:每次出手时从射程内敌人中随机锁定一个(错峰施放时实时索敌,目标状态最新鲜);
|
||||
* - unique:优先锁定本轮未选过的敌人,敌人不足一轮时重置去重记录再来一轮;
|
||||
* 实时索敌失败(敌人死亡/移动出射程)时回退施法时锁定的目标点,保证技能不落空。
|
||||
*/
|
||||
private resolveSummonCastTargetPos(config: SkillConfig, heroAttrs: HeroAttrsComp, heroView: HeroViewComp | null, baseTargetPos: Vec3 | null, castIndex: number, summon: SummonParams | undefined, usedEids: Set<number>): Vec3 | null {
|
||||
if (!summon || summon.count <= 1 || summon.target_mode === SummonTMode.same) {
|
||||
return this.resolveRepeatCastTargetPos(baseTargetPos, castIndex, summon);
|
||||
}
|
||||
const maxRange = this.resolveMaxCastRange(heroAttrs, heroAttrs.type as HType);
|
||||
const enemies = this.collectEnemiesInRange(heroAttrs, heroView, maxRange);
|
||||
const picked = enemies.length > 0 ? this.pickEnemyByTargetMode(enemies, summon.target_mode, usedEids) : null;
|
||||
if (picked?.node) {
|
||||
mLogger.log(this.debugMode, "SCastSystem", `[召唤索敌] 「${config.name}」第${castIndex + 1}个实体锁定目标 eid:${picked.ent?.eid} 模式:${SummonTMode[summon.target_mode]}`);
|
||||
// 与 resolveEnemyCastTargetPos 保持一致:贝塞尔取脚底原始坐标,其余补半身高命中身体中心
|
||||
const pos = config.RType === RType.bezier ? picked.node.position.clone() : this.buildEnemyCastTargetPos(picked);
|
||||
// 召唤偏移铺开仍生效:random 模式重复锁定同一敌人时,避免实体完全重叠
|
||||
const offset = this.resolveCastOffset(castIndex, summon);
|
||||
pos.x += offset.x;
|
||||
pos.y += offset.y;
|
||||
return pos;
|
||||
}
|
||||
// 实时索敌失败:回退施法时锁定的目标点
|
||||
return this.resolveRepeatCastTargetPos(baseTargetPos, castIndex, summon);
|
||||
}
|
||||
|
||||
/** 按目标模式从射程内候选敌人中挑选本次施放的锁定目标,并写入去重记录 */
|
||||
private pickEnemyByTargetMode(enemies: HeroViewComp[], mode: SummonTMode, usedEids: Set<number>): HeroViewComp | null {
|
||||
let pool = enemies;
|
||||
if (mode === SummonTMode.unique) {
|
||||
const unused = enemies.filter(view => !view.ent || !usedEids.has(view.ent.eid));
|
||||
if (unused.length > 0) {
|
||||
pool = unused;
|
||||
} else {
|
||||
// 敌人不足一轮:重置已选记录,重新开始去重,保证尽量分散
|
||||
usedEids.clear();
|
||||
}
|
||||
}
|
||||
const picked = pool[Math.floor(Math.random() * pool.length)];
|
||||
if (picked?.ent) usedEids.add(picked.ent.eid);
|
||||
return picked ?? null;
|
||||
}
|
||||
|
||||
/** 收集施法者射程内的全部敌方目标(随机/去重目标模式实时索敌用),网格遍历与 findNearestEnemyByGrid 同源 */
|
||||
private collectEnemiesInRange(heroAttrs: HeroAttrsComp, heroView: HeroViewComp | null, maxRange: number): HeroViewComp[] {
|
||||
const result: HeroViewComp[] = [];
|
||||
if (!heroView?.node) return result;
|
||||
const grid = heroAttrs.fac === FacSet.HERO ? smc.mission.monGrid : smc.mission.heroGrid;
|
||||
const currentX = heroView.node.position.x;
|
||||
for (const eid of grid) {
|
||||
if (eid < 0) continue;
|
||||
const entity = ecs.getEntityByEid(eid);
|
||||
if (!entity) continue;
|
||||
const model = entity.get(HeroAttrsComp);
|
||||
const view = entity.get(HeroViewComp);
|
||||
if (!model || model.is_dead || model.is_reviving || !view?.node) continue;
|
||||
if (Math.abs(currentX - view.node.position.x) <= maxRange) {
|
||||
result.push(view);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 解析第 castIndex 个实体的偏移:召唤技能按 prefab 配置对称交替铺开(0, +1, -1, +2, -2... 档 × 步进值);多发回落纵向扇形档位 */
|
||||
private resolveCastOffset(castIndex: number, summon?: SummonParams): { x: number; y: number } {
|
||||
if (summon && summon.count > 1) {
|
||||
// 对称交替:偶数索引(>=2)取负档,奇数索引取正档,使召唤物以原点为中心向两侧展开
|
||||
const level = Math.ceil(castIndex / 2);
|
||||
const sign = castIndex % 2 === 0 ? -1 : 1;
|
||||
const factor = castIndex === 0 ? 0 : sign * level;
|
||||
return { x: factor * summon.x_offset, y: factor * summon.y_offset };
|
||||
}
|
||||
return { x: 0, y: SCastSystem.REPEAT_Y_OFFSETS[castIndex % SCastSystem.REPEAT_Y_OFFSETS.length] };
|
||||
}
|
||||
|
||||
/** 构建技能尝试顺序:优先主动技能,其次普攻 */
|
||||
@@ -518,12 +634,12 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
* 创建技能实体(投射物/范围体等)。
|
||||
* 仅用于对敌伤害技能的实体化表现与碰撞伤害分发。
|
||||
*/
|
||||
private createSkillEntity(s_uuid: number, skillLv: number, caster: HeroViewComp, cAttrsComp: HeroAttrsComp, targetPos: Vec3, castIndex: number = 0, overrides?: SkillOverrides) {
|
||||
private createSkillEntity(s_uuid: number, skillLv: number, caster: HeroViewComp, cAttrsComp: HeroAttrsComp, targetPos: Vec3, castIndex: number = 0, overrides?: SkillOverrides, summon?: SummonParams) {
|
||||
if (!caster.node || !caster.node.isValid) return;
|
||||
const parent = caster.node.parent;
|
||||
if (!parent) return;
|
||||
const skill = ecs.getEntity<Skill>(Skill);
|
||||
const startPos = this.resolveRepeatCastStartPos(caster.node.position, castIndex);
|
||||
const startPos = this.resolveRepeatCastStartPos(caster.node.position, castIndex, summon);
|
||||
skill.load(startPos, parent, s_uuid, targetPos.clone(), caster, cAttrsComp, skillLv, 0, overrides);
|
||||
}
|
||||
|
||||
@@ -531,17 +647,17 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
* 对敌技能效果处理。
|
||||
* 当前职责:仅处理伤害技能并创建技能实体。
|
||||
*/
|
||||
private applyEnemySkillEffects(s_uuid: number, skillLv: number, config: SkillConfig, heroView: HeroViewComp, cAttrsComp: HeroAttrsComp, targetPos: Vec3 | null, castIndex: number = 0, overrides?: SkillOverrides) {
|
||||
private applyEnemySkillEffects(s_uuid: number, skillLv: number, config: SkillConfig, heroView: HeroViewComp, cAttrsComp: HeroAttrsComp, targetPos: Vec3 | null, castIndex: number = 0, overrides?: SkillOverrides, summon?: SummonParams) {
|
||||
const kind = config.kind ?? SkillKind.Damage;
|
||||
if (kind !== SkillKind.Damage) return;
|
||||
if (config.ap <= 0 || !targetPos) return;
|
||||
this.createSkillEntity(s_uuid, skillLv, heroView, cAttrsComp, targetPos, castIndex, overrides);
|
||||
this.createSkillEntity(s_uuid, skillLv, heroView, cAttrsComp, targetPos, castIndex, overrides, summon);
|
||||
}
|
||||
|
||||
private resolveRepeatCastStartPos(startPos: Readonly<Vec3>, castIndex: number): Vec3 {
|
||||
const offset = SCastSystem.REPEAT_Y_OFFSETS[castIndex % SCastSystem.REPEAT_Y_OFFSETS.length];
|
||||
if (offset === 0) return startPos.clone();
|
||||
return new Vec3(startPos.x, startPos.y + offset, startPos.z);
|
||||
private resolveRepeatCastStartPos(startPos: Readonly<Vec3>, castIndex: number, summon?: SummonParams): Vec3 {
|
||||
const offset = this.resolveCastOffset(castIndex, summon);
|
||||
if (offset.x === 0 && offset.y === 0) return startPos.clone();
|
||||
return new Vec3(startPos.x + offset.x, startPos.y + offset.y, startPos.z);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -841,7 +957,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
return nearestEnemy.node?.position.clone() ?? null;
|
||||
}
|
||||
if (config.DTType !== DTType.range) {
|
||||
return this.buildEnemyCastTargetPos(heroView, nearestEnemy, maxRange);
|
||||
return this.buildEnemyCastTargetPos(nearestEnemy);
|
||||
}
|
||||
const frontEnemy = this.findFrontEnemyInRange(heroAttrs, heroView, maxRange, nearestEnemy);
|
||||
if (!frontEnemy?.node) return null;
|
||||
@@ -855,19 +971,10 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
return heroAttrs.dis;
|
||||
}
|
||||
|
||||
/** 生成沿目标方向的施法目标坐标 */
|
||||
private buildEnemyCastTargetPos(caster: HeroViewComp, target: HeroViewComp, castRange: number): Vec3 {
|
||||
// 直接返回目标的真实坐标,保留其 Y 轴信息,确保能向目标真实所在位置发射
|
||||
// 考虑到目前角色的 y 坐标都是脚底(碰撞体底部),为了命中身体中心,给目标 y 加上高度的一半
|
||||
let halfHeight = 0;
|
||||
if (target.node) {
|
||||
const transform = target.node.getComponent('cc.UITransform') as any;
|
||||
if (transform) {
|
||||
halfHeight = transform.height / 2;
|
||||
} else {
|
||||
halfHeight = 40; // 如果没有 UITransform,给一个默认高度偏移
|
||||
}
|
||||
}
|
||||
/** 生成对敌施法目标坐标:直接返回目标的真实坐标;y 为脚底(碰撞体底部),补半身高以命中身体中心 */
|
||||
private buildEnemyCastTargetPos(target: HeroViewComp): Vec3 {
|
||||
const transform = target.node?.getComponent(UITransform);
|
||||
const halfHeight = transform ? transform.height / 2 : 40; // 无 UITransform 时给默认高度偏移
|
||||
|
||||
const pos = target.node.position.clone();
|
||||
pos.y += halfHeight;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { SkillTriggerType } from "../common/config/heroSet";
|
||||
import { ResolvedTriggerEntry, SkillTriggerType } from "../common/config/heroSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { FacSet } from "../common/config/GameSet";
|
||||
@@ -98,6 +98,7 @@ export class SkillTriggerHelper {
|
||||
/**
|
||||
* 处理攻击附加技能
|
||||
* 判定逻辑:依据配置中的 t_num(触发所需普攻次数),对比当前的普攻累计次数取模判定。
|
||||
* 天赋修正:L4 减少 t_num 阈值(下限 1);L5 按概率额外触发 1 次。
|
||||
*/
|
||||
private static handleAtking(model: HeroAttrsComp, view: HeroViewComp) {
|
||||
if (!model.runtime_atking || model.runtime_atking.length === 0) return;
|
||||
@@ -107,10 +108,18 @@ export class SkillTriggerHelper {
|
||||
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.AtkCount);
|
||||
}
|
||||
triggerCount = Math.max(1, Math.floor(triggerCount));
|
||||
// L5 天赋:概率额外触发 1 次(与羁绊次数加成独立乘算)
|
||||
if (model.talent_extra_trigger_chance > 0 && Math.random() * 100 < model.talent_extra_trigger_chance) {
|
||||
triggerCount += 1;
|
||||
}
|
||||
// L4 天赋:减少触发所需次数(下限 1)
|
||||
const tNumReduce = model.talent_t_num_reduce;
|
||||
|
||||
model.runtime_atking.forEach(atkConfig => {
|
||||
// atk_count 代表已进行的普攻次数。当其余数刚好整除配置阈值时触发。
|
||||
if (model.atk_count > 0 && model.atk_count % atkConfig.t_num === 0) {
|
||||
const tNum = Math.max(1, atkConfig.t_num - tNumReduce);
|
||||
if (model.atk_count > 0 && model.atk_count % tNum === 0
|
||||
&& this.rollTriggerChance(model, SkillTriggerType.Atking, atkConfig)) {
|
||||
for (let i = 0; i < triggerCount; i++) {
|
||||
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atking, atkConfig.overrides);
|
||||
}
|
||||
@@ -121,6 +130,7 @@ export class SkillTriggerHelper {
|
||||
/**
|
||||
* 处理受击附加技能
|
||||
* 判定逻辑:依据配置中的 t_num(触发所需受击次数),对比当前的受击累计次数取模判定。
|
||||
* 天赋修正:L4 减少 t_num 阈值(下限 1);L5 按概率额外触发 1 次。
|
||||
*/
|
||||
private static handleAtked(model: HeroAttrsComp, view: HeroViewComp) {
|
||||
if (!model.runtime_atked || model.runtime_atked.length === 0) return;
|
||||
@@ -130,10 +140,18 @@ export class SkillTriggerHelper {
|
||||
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.BeAtkCount);
|
||||
}
|
||||
triggerCount = Math.max(1, Math.floor(triggerCount));
|
||||
// L5 天赋:概率额外触发 1 次(与羁绊次数加成独立乘算)
|
||||
if (model.talent_extra_trigger_chance > 0 && Math.random() * 100 < model.talent_extra_trigger_chance) {
|
||||
triggerCount += 1;
|
||||
}
|
||||
// L4 天赋:减少触发所需次数(下限 1)
|
||||
const tNumReduce = model.talent_t_num_reduce;
|
||||
|
||||
model.runtime_atked.forEach(atkConfig => {
|
||||
// atked_count 代表已承受的受击次数。当其余数刚好整除配置阈值时触发。
|
||||
if (model.atked_count > 0 && model.atked_count % atkConfig.t_num === 0) {
|
||||
const tNum = Math.max(1, atkConfig.t_num - tNumReduce);
|
||||
if (model.atked_count > 0 && model.atked_count % tNum === 0
|
||||
&& this.rollTriggerChance(model, SkillTriggerType.Atked, atkConfig)) {
|
||||
for (let i = 0; i < triggerCount; i++) {
|
||||
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atked, atkConfig.overrides);
|
||||
}
|
||||
@@ -141,6 +159,31 @@ export class SkillTriggerHelper {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发概率判定 + 保底叠加(仅 Atking/Atked 消费)
|
||||
* 次数条件达成后按 t_chance(缺省 100 必触发,兼容存量配置)掷骰;
|
||||
* 未命中且配置 pity_add > 0 时,为该 s_uuid 累积下次概率增量,形成"越不触发越容易触发"的保底体验;
|
||||
* 触发成功后清空叠加,最终概率封顶 100。
|
||||
*
|
||||
* @param model 施法者属性组件(保底缓存挂载于此)
|
||||
* @param type 触发类型(决定读/写哪份保底缓存)
|
||||
* @param config 当前生效的触发条目
|
||||
* @returns 是否通过概率判定
|
||||
*/
|
||||
private static rollTriggerChance(model: HeroAttrsComp, type: SkillTriggerType, config: ResolvedTriggerEntry): boolean {
|
||||
const pityMap = type === SkillTriggerType.Atking ? model.pity_atking : model.pity_atked;
|
||||
const chance = Math.min(100, (config.t_chance ?? 100) + (pityMap[config.s_uuid] ?? 0));
|
||||
if (chance >= 100 || Math.random() * 100 < chance) {
|
||||
pityMap[config.s_uuid] = 0; // 触发成功:清空保底叠加
|
||||
return true;
|
||||
}
|
||||
const pityAdd = config.pity_add ?? 0;
|
||||
if (pityAdd > 0) {
|
||||
pityMap[config.s_uuid] = (pityMap[config.s_uuid] ?? 0) + pityAdd;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的数组型触发器处理(适用于 FStart 等无需额外判定的简单列表触发)
|
||||
*/
|
||||
|
||||
686
assets/script/game/map/HeroInfoComp.ts
Normal file
686
assets/script/game/map/HeroInfoComp.ts
Normal file
@@ -0,0 +1,686 @@
|
||||
/**
|
||||
* @file HeroInfoComp.ts
|
||||
* @description 英雄详情弹窗(UI 视图层,oops.gui 弹窗)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 展示单个英雄的详情:idle 动画、名称、AP / DEF / HP / CD、技能描述、
|
||||
* 稀有度底框、局外等级、职业、碎片数量、升级按钮、技能图标列表。
|
||||
* 2. 提供升级操作(消耗碎片提升局外等级,成功后播放升级特效)。
|
||||
* 3. 提供前后英雄切换按钮,按图鉴排序顺序循环切换(品质降序 → uuid 升序)。
|
||||
* 4. 提供关闭按钮 / 遮罩点击关闭弹窗。
|
||||
*
|
||||
* 使用(oops.gui 模块):
|
||||
* oops.gui.open(UIID.HeroInfo, { uuid: 5001 });
|
||||
*
|
||||
* 关键设计:
|
||||
* - 详情渲染逻辑与原 HerosListComp 详情面板同源(已迁移至此),
|
||||
* HerosListComp 仅保留卡片列表,点击卡片改为打开本弹窗。
|
||||
* - 前后切换按品质降序 → uuid 升序循环(与图鉴页面卡片顺序一致)。
|
||||
* - hero_icon 使用 Animation + iconVisualToken 机制防止异步加载竞态。
|
||||
*
|
||||
* 依赖:
|
||||
* - HeroInfo / HeroList(heroSet)—— 英雄静态配置与全量 UUID 列表
|
||||
* - smc.getHeroGrowth / smc.getHeroOuterLv / smc.upgradeHero —— 成长数据
|
||||
* - buildHeroSkillDesc(HeroSkillText)—— 技能描述生成器
|
||||
* - SSkillBoxComp / ChipComp / SmallTipComp —— 子组件
|
||||
*/
|
||||
import { _decorator, Animation, AnimationClip, Button, EventTouch, Label, Node, NodeEventType, Prefab, RichText, Sprite, Tween, Vec3, instantiate, resources, tween } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { HeroInfo, HeroList, SkillTriggerType, LvFieldEntry, LvTriggerEntry, TriggerGrouped, HSkillInfo, getHeroDisplayStats, LvBonusAttrsEntry } from "../common/config/heroSet";
|
||||
import { buildHeroSkillDesc } from "../common/HeroSkillText";
|
||||
import { outlineRich } from "../common/RichOutline";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { HERO_UPGRADE_NEED_FRAGMENTS, RARITY_BG_COLORS } from "../common/config/GameSet";
|
||||
import { ChipComp } from "./ChipComp";
|
||||
import { SmallTipComp } from "./SmallTipComp";
|
||||
import { SSkillBoxComp } from "./SSkillBoxComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
|
||||
const { property, ccclass } = _decorator;
|
||||
|
||||
/** HeroInfo 弹窗初始化参数 */
|
||||
export interface HeroInfoParams {
|
||||
/** 英雄模板 uuid */
|
||||
uuid: number;
|
||||
}
|
||||
|
||||
@ccclass('HeroInfoComp')
|
||||
@ecs.register('HeroInfoComp', false)
|
||||
export class HeroInfoComp extends CCComp {
|
||||
// ======================== 编辑器绑定节点 ========================
|
||||
|
||||
@property(Node)
|
||||
hero_icon = null!
|
||||
|
||||
@property(Node)
|
||||
ap_node = null!
|
||||
|
||||
@property(Node)
|
||||
def_node = null!
|
||||
|
||||
@property(Node)
|
||||
hp_node = null!
|
||||
|
||||
@property(Node)
|
||||
cd_node = null!
|
||||
|
||||
/** 卡牌背景底框节点(按卡池等级切换子节点显示) */
|
||||
@property(Node)
|
||||
BG_node = null!
|
||||
|
||||
@property(Node)
|
||||
info_node = null!
|
||||
|
||||
@property(Node)
|
||||
name_node = null!
|
||||
|
||||
/** 卡池等级 Label,直接显示 X */
|
||||
@property(Label)
|
||||
lv_label = null!
|
||||
|
||||
/** 职业展示节点(子节点按 HRole 枚举值命名 0~5) */
|
||||
@property(Node)
|
||||
role_node = null!
|
||||
|
||||
/** 升级按钮节点(smalltip 气泡已作为其子节点挂在下面) */
|
||||
@property(Node)
|
||||
upgrade_btn = null!
|
||||
|
||||
/** 碎片图标节点(挂载 ChipComp 用于渲染碎片图标) */
|
||||
@property(Node)
|
||||
chip_node = null!
|
||||
|
||||
/** 技能描述节点 */
|
||||
@property(Node)
|
||||
infos_node = null!
|
||||
|
||||
/** 技能图标容器节点 */
|
||||
@property(Node)
|
||||
skills_node = null!
|
||||
|
||||
/** 技能小图标预制体(根节点挂 SSkillBoxComp) */
|
||||
@property(Prefab)
|
||||
skill_icon_prefab = null!
|
||||
|
||||
/** 升级特效动画节点(内含升级效果 Animation,点击升级时从头播放) */
|
||||
@property(Node)
|
||||
upgrade_anim_node = null!
|
||||
|
||||
/** 碎片需求/总量 Label(显示 当前碎片 / 下级需求) */
|
||||
@property(Label)
|
||||
fragment_label = null!
|
||||
|
||||
/** 升级提升加成说明 RichText(展示升级后的属性提升,数值绿色高亮) */
|
||||
@property(RichText)
|
||||
upgrade_info_label = null!
|
||||
|
||||
/** 关闭按钮节点 */
|
||||
@property({ type: Node, displayName: "关闭按钮" })
|
||||
close_btn: Node = null!
|
||||
|
||||
/** 上一个英雄切换按钮节点 */
|
||||
@property({ type: Node, displayName: "上一个英雄按钮" })
|
||||
prev_btn: Node = null!
|
||||
|
||||
/** 下一个英雄切换按钮节点 */
|
||||
@property({ type: Node, displayName: "下一个英雄按钮" })
|
||||
next_btn: Node = null!
|
||||
|
||||
/** 全屏遮罩节点(点击弹窗主体以外区域关闭,可选) */
|
||||
@property({ type: Node, displayName: "遮罩节点" })
|
||||
mask_node: Node = null!
|
||||
|
||||
// ======================== 运行时状态 ========================
|
||||
|
||||
/** 当前展示的英雄模板 uuid */
|
||||
huuid: number = 0
|
||||
private iconVisualToken: number = 0
|
||||
debugMode: boolean = false
|
||||
|
||||
// ======================== 生命周期 ========================
|
||||
|
||||
protected onLoad(): void {
|
||||
// 关闭按钮点击
|
||||
if (this.close_btn && this.close_btn.isValid) {
|
||||
this.close_btn.on(NodeEventType.TOUCH_END, this.onCloseClick, this);
|
||||
}
|
||||
// 前后切换按钮
|
||||
if (this.prev_btn && this.prev_btn.isValid) {
|
||||
this.prev_btn.on(NodeEventType.TOUCH_END, this.onPrevClick, this);
|
||||
}
|
||||
if (this.next_btn && this.next_btn.isValid) {
|
||||
this.next_btn.on(NodeEventType.TOUCH_END, this.onNextClick, this);
|
||||
}
|
||||
// 遮罩点击关闭
|
||||
if (this.mask_node && this.mask_node.isValid) {
|
||||
this.mask_node.on(NodeEventType.TOUCH_END, this.onMaskClick, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* oops.gui 添加到舞台回调(框架约定入口)
|
||||
* @param params 见 HeroInfoParams
|
||||
*/
|
||||
onAdded(params: HeroInfoParams) {
|
||||
this.huuid = params?.uuid ?? 0;
|
||||
this.bindUpgradeBtn();
|
||||
if (this.upgrade_anim_node) this.upgrade_anim_node.active = false;
|
||||
if (this.huuid) {
|
||||
this.updateHeroDetail(this.huuid);
|
||||
}
|
||||
}
|
||||
|
||||
protected onEnable(): void {
|
||||
oops.message.on(GameEvent.HERO_GROWTH_UPDATE, this.onGrowthUpdate, this);
|
||||
}
|
||||
|
||||
protected onDisable(): void {
|
||||
oops.message.off(GameEvent.HERO_GROWTH_UPDATE, this.onGrowthUpdate, this);
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
super.onDestroy();
|
||||
if (this.close_btn && this.close_btn.isValid) {
|
||||
this.close_btn.off(NodeEventType.TOUCH_END, this.onCloseClick, this);
|
||||
}
|
||||
if (this.prev_btn && this.prev_btn.isValid) {
|
||||
this.prev_btn.off(NodeEventType.TOUCH_END, this.onPrevClick, this);
|
||||
}
|
||||
if (this.next_btn && this.next_btn.isValid) {
|
||||
this.next_btn.off(NodeEventType.TOUCH_END, this.onNextClick, this);
|
||||
}
|
||||
if (this.mask_node && this.mask_node.isValid) {
|
||||
this.mask_node.off(NodeEventType.TOUCH_END, this.onMaskClick, this);
|
||||
}
|
||||
if (this.upgrade_btn && this.upgrade_btn.isValid) {
|
||||
this.upgrade_btn.off(Button.EventType.CLICK, this.onUpgradeClick, this);
|
||||
}
|
||||
}
|
||||
|
||||
private onGrowthUpdate() {
|
||||
if (this.huuid) {
|
||||
this.updateHeroDetail(this.huuid);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 按钮回调 ========================
|
||||
|
||||
/** 关闭按钮点击 */
|
||||
private onCloseClick(event: EventTouch) {
|
||||
event.propagationStopped = true;
|
||||
oops.audio.playEffect("music/button");
|
||||
this.close();
|
||||
}
|
||||
|
||||
/** 遮罩点击关闭 */
|
||||
private onMaskClick(event: EventTouch) {
|
||||
event.propagationStopped = true;
|
||||
oops.audio.playEffect("music/button");
|
||||
this.close();
|
||||
}
|
||||
|
||||
/** 关闭弹窗 */
|
||||
private close() {
|
||||
if (!this.node || !this.node.isValid || !this.node.parent) return;
|
||||
oops.gui.removeByNode(this.node);
|
||||
}
|
||||
|
||||
/** 切换到上一个英雄(按图鉴排序循环) */
|
||||
private onPrevClick(event: EventTouch) {
|
||||
event.propagationStopped = true;
|
||||
oops.audio.playEffect("music/button");
|
||||
this.switchHero(-1);
|
||||
}
|
||||
|
||||
/** 切换到下一个英雄(按图鉴排序循环) */
|
||||
private onNextClick(event: EventTouch) {
|
||||
event.propagationStopped = true;
|
||||
oops.audio.playEffect("music/button");
|
||||
this.switchHero(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按图鉴排序顺序循环切换英雄
|
||||
* @param delta 1 下一个,-1 上一个
|
||||
*/
|
||||
private switchHero(delta: 1 | -1) {
|
||||
// 与图鉴页面排序同源:按职业(HRole)升序 → uuid 升序
|
||||
const sorted = [...HeroList].sort((a, b) => {
|
||||
const heroA = HeroInfo[a];
|
||||
const heroB = HeroInfo[b];
|
||||
const roleA = heroA?.role ?? 0;
|
||||
const roleB = heroB?.role ?? 0;
|
||||
if (roleA !== roleB) return roleA - roleB;
|
||||
return a - b;
|
||||
});
|
||||
if (sorted.length === 0) return;
|
||||
const idx = sorted.indexOf(this.huuid);
|
||||
// 当前 uuid 不在列表中(异常)时回退到第一个
|
||||
const cur = idx >= 0 ? idx : 0;
|
||||
const next = (cur + delta + sorted.length) % sorted.length;
|
||||
this.huuid = sorted[next];
|
||||
this.updateHeroDetail(this.huuid);
|
||||
}
|
||||
|
||||
// ======================== 升级 ========================
|
||||
|
||||
/**
|
||||
* 绑定升级按钮点击事件
|
||||
* 在 onAdded 中调用,确保 @property 属性已正确赋值
|
||||
*/
|
||||
private bindUpgradeBtn() {
|
||||
if (!this.upgrade_btn) {
|
||||
mLogger.error(this.debugMode, "HeroInfoComp", "upgrade_btn 未绑定,请检查编辑器属性面板");
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保 smalltip 气泡初始隐藏,避免遮挡按钮触摸(组件缺失时兜底补挂)
|
||||
const tipNode = this.upgrade_btn.getChildByName("smalltip");
|
||||
if (tipNode) {
|
||||
tipNode.getComponent(SmallTipComp) ?? tipNode.addComponent(SmallTipComp);
|
||||
tipNode.active = false;
|
||||
}
|
||||
|
||||
let btn = this.upgrade_btn.getComponent(Button);
|
||||
if (!btn) btn = this.upgrade_btn.addComponent(Button);
|
||||
btn.interactable = true;
|
||||
this.upgrade_btn.off(Button.EventType.CLICK, this.onUpgradeClick, this);
|
||||
this.upgrade_btn.on(Button.EventType.CLICK, this.onUpgradeClick, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级按钮点击回调
|
||||
* 消耗碎片提升英雄局外等级,成功后刷新详情面板
|
||||
*/
|
||||
onUpgradeClick() {
|
||||
const uuid = this.huuid;
|
||||
if (!uuid) return;
|
||||
|
||||
this.playUpgradeBtnAnim();
|
||||
|
||||
const success = smc.upgradeHero(uuid);
|
||||
if (success) {
|
||||
mLogger.log(this.debugMode, "HeroInfoComp", `英雄 ${uuid} 升级成功`);
|
||||
this.playUpgradeAnim();
|
||||
this.updateHeroDetail(uuid);
|
||||
} else {
|
||||
const growth = smc.getHeroGrowth(uuid);
|
||||
const need = HERO_UPGRADE_NEED_FRAGMENTS[growth.lv + 1];
|
||||
// 碎片不足 / 已满级:气泡提示
|
||||
const tipText = need === undefined ? "已满级" : "碎片不足";
|
||||
this.showSmallTip(tipText);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放升级按钮按压回弹动画
|
||||
*/
|
||||
private playUpgradeBtnAnim() {
|
||||
if (!this.upgrade_btn) return;
|
||||
Tween.stopAllByTarget(this.upgrade_btn);
|
||||
this.upgrade_btn.setScale(1, 1, 1);
|
||||
tween(this.upgrade_btn)
|
||||
.to(0.08, { scale: new Vec3(0.9, 0.9, 1) }, { easing: 'quadOut' })
|
||||
.to(0.12, { scale: new Vec3(1.1, 1.1, 1) }, { easing: 'quadOut' })
|
||||
.to(0.08, { scale: new Vec3(1, 1, 1) }, { easing: 'quadIn' })
|
||||
.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放升级特效动画
|
||||
* 每次升级成功均从头播放;动画未播完时再次升级,立即重新开始
|
||||
*/
|
||||
private playUpgradeAnim() {
|
||||
if (!this.upgrade_anim_node) return;
|
||||
const anim = this.upgrade_anim_node.getComponent(Animation);
|
||||
const clip = anim?.clips?.[0];
|
||||
if (!anim || !clip) {
|
||||
mLogger.log(this.debugMode, "HeroInfoComp", "playUpgradeAnim: 缺少 Animation 组件或动画剪辑");
|
||||
return;
|
||||
}
|
||||
|
||||
this.upgrade_anim_node.active = true;
|
||||
anim.stop();
|
||||
|
||||
const animState = anim.getState(clip.name);
|
||||
if (animState) {
|
||||
animState.off('finished', this.onUpgradeAnimFinished, this);
|
||||
animState.once('finished', this.onUpgradeAnimFinished, this);
|
||||
}
|
||||
anim.play(clip.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级特效动画播放结束回调:隐藏特效节点
|
||||
*/
|
||||
private onUpgradeAnimFinished() {
|
||||
if (this.upgrade_anim_node && this.upgrade_anim_node.isValid) {
|
||||
this.upgrade_anim_node.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在升级按钮附近弹出 smalltip 气泡提示
|
||||
* @param text 提示文案
|
||||
*/
|
||||
private showSmallTip(text: string) {
|
||||
const tipNode = this.upgrade_btn?.getChildByName("smalltip");
|
||||
if (!tipNode || !tipNode.isValid) {
|
||||
mLogger.log(this.debugMode, "HeroInfoComp", `未在 upgrade_btn 下找到 smalltip 节点,无法提示:${text}`);
|
||||
return;
|
||||
}
|
||||
const tip = tipNode.getComponent(SmallTipComp) ?? tipNode.addComponent(SmallTipComp);
|
||||
tip.show(text);
|
||||
}
|
||||
|
||||
// ======================== 详情面板 ========================
|
||||
|
||||
private updateHeroDetail(uuid: number) {
|
||||
const hero = HeroInfo[uuid];
|
||||
if (!hero) return;
|
||||
|
||||
// 属性按当前局外等级计算
|
||||
const heroLv = Math.max(1, smc.getHeroOuterLv(uuid));
|
||||
this.setLabelText(this.name_node, hero.name || "");
|
||||
const stats = getHeroDisplayStats(hero, heroLv);
|
||||
this.setLabelText(this.ap_node, `${stats.ap}`);
|
||||
this.setLabelText(this.def_node, `${stats.def}`);
|
||||
this.setLabelText(this.hp_node, `${stats.hp}`);
|
||||
|
||||
this.updateCdDisplay(hero);
|
||||
|
||||
if (this.info_node) {
|
||||
// Label 承载:HeroSkillText 输出富文本,剥离 BBCode 标签转纯文本
|
||||
const desc = buildHeroSkillDesc(hero.uuid, true, true).replace(/<[^>]+>/g, "");
|
||||
const infoLabel = this.info_node.getChildByName("info")?.getComponent(Label)
|
||||
|| this.info_node.getComponent(Label)
|
||||
|| this.info_node.getComponentInChildren(Label);
|
||||
if (infoLabel) infoLabel.string = desc || hero.info || "";
|
||||
}
|
||||
|
||||
this.updatePoolLvBg(hero.rarity ?? 1);
|
||||
this.updateLvDisplay(uuid);
|
||||
this.updateRoleDisplay(hero);
|
||||
this.updateHeroAnimation(uuid);
|
||||
this.updateGrowthPanel(uuid);
|
||||
this.updateSkillIcons(hero);
|
||||
}
|
||||
|
||||
private updatePoolLvBg(rarity: number) {
|
||||
if (!this.BG_node) return;
|
||||
// BG_node 下直接放置颜色节点(green/blue/purple/yellow/red),按卡池等级激活对应颜色
|
||||
const color = RARITY_BG_COLORS[Math.min(rarity, 5) - 1];
|
||||
this.BG_node.children.forEach(child => {
|
||||
child.active = (child.name === color);
|
||||
});
|
||||
}
|
||||
|
||||
private updateCdDisplay(hero: typeof HeroInfo[number]) {
|
||||
if (!this.cd_node) return;
|
||||
const skillKeys = Object.keys(hero.skills);
|
||||
if (skillKeys.length === 0) return;
|
||||
const firstSkill = hero.skills[Number(skillKeys[0])];
|
||||
if (firstSkill) {
|
||||
this.setLabelText(this.cd_node, `${firstSkill.cd}s`);
|
||||
}
|
||||
}
|
||||
|
||||
private updateLvDisplay(uuid: number) {
|
||||
if (!this.lv_label) return;
|
||||
const outerLv = smc.getHeroOuterLv(uuid);
|
||||
this.lv_label.string = `${outerLv}`;
|
||||
}
|
||||
|
||||
private updateRoleDisplay(hero: typeof HeroInfo[number]) {
|
||||
if (!this.role_node) return;
|
||||
this.role_node.active = true;
|
||||
const roleStr = `${hero.role ?? 0}`;
|
||||
this.role_node.children.forEach(child => {
|
||||
child.active = (child.name === roleStr);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新局外成长面板:碎片数量与升级按钮状态
|
||||
* @param uuid 英雄模板 uuid
|
||||
*/
|
||||
private updateGrowthPanel(uuid: number) {
|
||||
const growth = smc.getHeroGrowth(uuid);
|
||||
const needFragments = HERO_UPGRADE_NEED_FRAGMENTS[growth.lv + 1];
|
||||
const isMaxLv = needFragments === undefined;
|
||||
|
||||
// 碎片数量显示:当前拥有 / 下一级所需(满级时显示 MAX)
|
||||
const fragmentText = isMaxLv
|
||||
? `${growth.fragments} / MAX`
|
||||
: `${growth.fragments} / ${needFragments}`;
|
||||
if (this.fragment_label) {
|
||||
this.fragment_label.string = fragmentText;
|
||||
}
|
||||
|
||||
// 升级提升加成说明
|
||||
this.updateUpgradeInfoLabel(uuid, growth.lv, isMaxLv);
|
||||
|
||||
// 碎片图标:使用 ChipComp 渲染英雄图标
|
||||
if (this.chip_node) {
|
||||
const chipComp = this.chip_node.getComponent(ChipComp) || this.chip_node.addComponent(ChipComp);
|
||||
chipComp.setHeroByUuid(uuid);
|
||||
}
|
||||
|
||||
// 升级按钮状态:始终可点击,碎片不足时通过气泡提示
|
||||
if (this.upgrade_btn) {
|
||||
this.upgrade_btn.active = true;
|
||||
const btnComp = this.upgrade_btn.getComponent(Button);
|
||||
if (btnComp) btnComp.interactable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 技能图标列表 ========================
|
||||
|
||||
/**
|
||||
* 刷新升级提升加成说明 RichText
|
||||
* 展示口径:对比当前等级与下一等级,仅列出 AP/HP/DEF 属性差值,单行展示
|
||||
*/
|
||||
private updateUpgradeInfoLabel(uuid: number, curLv: number, isMaxLv: boolean) {
|
||||
if (!this.upgrade_info_label) return;
|
||||
const hero = HeroInfo[uuid];
|
||||
if (!hero) { this.upgrade_info_label.string = ""; return; }
|
||||
|
||||
if (isMaxLv) {
|
||||
this.upgrade_info_label.string = outlineRich("已满级");
|
||||
return;
|
||||
}
|
||||
|
||||
const nextLv = curLv + 1;
|
||||
const cur = getHeroDisplayStats(hero, curLv);
|
||||
const nxt = getHeroDisplayStats(hero, nextLv);
|
||||
|
||||
// 属性提升差值:单行展示 AP/HP/DEF,数值绿色高亮
|
||||
const parts: string[] = [];
|
||||
if (nxt.hp > cur.hp) parts.push(`生命<color=#00FF00>+${nxt.hp - cur.hp}</color>`);
|
||||
if (nxt.ap > cur.ap) parts.push(`攻击<color=#00FF00>+${nxt.ap - cur.ap}</color>`);
|
||||
if (nxt.def > cur.def) parts.push(`防御<color=#00FF00>+${nxt.def - cur.def}</color>`);
|
||||
|
||||
this.upgrade_info_label.string = parts.length > 0
|
||||
? outlineRich(`升级属性提升:${parts.join(" ")}`)
|
||||
: outlineRich(`升级至 Lv.${nextLv}`);
|
||||
}
|
||||
|
||||
/** 详情面板展示的触发技能槽位(按触发时机排序) */
|
||||
private static readonly PANEL_TRIGGER_TYPES: SkillTriggerType[] = [
|
||||
SkillTriggerType.Call,
|
||||
SkillTriggerType.FStart,
|
||||
SkillTriggerType.Atking,
|
||||
SkillTriggerType.Atked,
|
||||
SkillTriggerType.Dead,
|
||||
SkillTriggerType.FEnd,
|
||||
SkillTriggerType.Field,
|
||||
];
|
||||
|
||||
/**
|
||||
* 刷新当前英雄的技能图标列表:
|
||||
* 1. 大招:skills[1] 及以后(首个为普攻,跳过)。
|
||||
* 2. 触发技能:每个触发槽位整组视为一个技能。
|
||||
* 以局外等级为当前等级,未达解锁档的显示 locked。
|
||||
*/
|
||||
private updateSkillIcons(hero: typeof HeroInfo[number]) {
|
||||
if (!this.skills_node || !this.skill_icon_prefab) return;
|
||||
|
||||
// 清空旧图标(显式销毁,避免节点泄漏)
|
||||
for (let i = this.skills_node.children.length - 1; i >= 0; i--) {
|
||||
this.skills_node.children[i].destroy();
|
||||
}
|
||||
|
||||
const lv = Math.max(1, smc.getHeroOuterLv(hero.uuid));
|
||||
|
||||
// ---- 大招:skills[1] 及以后 ----
|
||||
Object.values(hero.skills).slice(1).forEach(hSkill => {
|
||||
const sLv = hSkill.lv_entries?.length
|
||||
? (hSkill.lv_entries.filter(e => e.lv <= lv).pop()?.s_lv ?? hSkill.lv)
|
||||
: hSkill.lv;
|
||||
this.addSkillIcon(hSkill.uuid, sLv, false, false);
|
||||
});
|
||||
|
||||
// ---- 普攻 lv2 强化档 ----
|
||||
this.addNormalLv2Icon(Object.values(hero.skills)[0], lv);
|
||||
|
||||
// ---- 触发技能 ----
|
||||
for (const type of HeroInfoComp.PANEL_TRIGGER_TYPES) {
|
||||
if (type === SkillTriggerType.Field) {
|
||||
this.addFieldGroupIcon(hero[SkillTriggerType.Field], lv);
|
||||
} else {
|
||||
this.addTriggerGroupIcon(hero[type] as TriggerGrouped | undefined, lv, type);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- bonus 档位 ----
|
||||
this.addBonusIcons(hero.bonus, lv);
|
||||
}
|
||||
|
||||
/**
|
||||
* bonus 档位图标:配置了 icon 的档位各出一个图标(追加在技能列表最末)。
|
||||
*/
|
||||
private addBonusIcons(bonus: LvBonusAttrsEntry[] | undefined, lv: number) {
|
||||
if (!bonus?.length) return;
|
||||
for (const entry of bonus) {
|
||||
if (!entry.icon) continue;
|
||||
const node = instantiate(this.skill_icon_prefab);
|
||||
node.name = `bonus_lv${entry.lv}`;
|
||||
const comp = node.getComponent(SSkillBoxComp) || node.addComponent(SSkillBoxComp);
|
||||
comp.initBonus(this.huuid, entry, lv < entry.lv);
|
||||
this.skills_node.addChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化技能小图标并初始化
|
||||
*/
|
||||
private addSkillIcon(s_uuid: number, s_lv: number, locked: boolean, isField: boolean, slot?: SkillTriggerType, isGroup: boolean = false, normalLv2: boolean = false, unlockLv?: number) {
|
||||
const node = instantiate(this.skill_icon_prefab);
|
||||
node.name = `skill_${s_uuid}`;
|
||||
const comp = node.getComponent(SSkillBoxComp) || node.addComponent(SSkillBoxComp);
|
||||
comp.initHeroSkill(s_uuid, s_lv, locked, isField, this.huuid, slot, isGroup, normalLv2, unlockLv);
|
||||
this.skills_node.addChild(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发技能槽位(call/fstart/atking/atked/dead)整组一个图标
|
||||
*/
|
||||
private addTriggerGroupIcon(grouped: TriggerGrouped | undefined, lv: number, type: SkillTriggerType) {
|
||||
if (!grouped) return;
|
||||
const keys = Object.keys(grouped).map(Number).filter(k => grouped[k]?.length);
|
||||
if (keys.length === 0) return;
|
||||
const sorted = [...grouped[keys[0]]].sort((a, b) => a.lv - b.lv);
|
||||
let active: LvTriggerEntry | undefined;
|
||||
for (const e of sorted) { if (e.lv <= lv) active = e; }
|
||||
let anyActive = !!active;
|
||||
for (const key of keys) {
|
||||
if (anyActive) break;
|
||||
for (const e of grouped[key] ?? []) {
|
||||
if (e.lv <= lv) { anyActive = true; break; }
|
||||
}
|
||||
}
|
||||
const pick = active ?? sorted[0];
|
||||
this.addSkillIcon(keys[0], pick.s_lv ?? 1, !anyActive, false, type, true, false, anyActive ? undefined : pick.lv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 驻场光环槽位整组一个图标
|
||||
*/
|
||||
private addFieldGroupIcon(field: LvFieldEntry[] | undefined, lv: number) {
|
||||
if (!field?.length) return;
|
||||
const sorted = [...field].sort((a, b) => a.lv - b.lv);
|
||||
let active: LvFieldEntry | undefined;
|
||||
for (const e of sorted) { if (e.lv <= lv) active = e; }
|
||||
const pick = active ?? sorted[0];
|
||||
const firstUuid = pick.uuids[0]?.uuid;
|
||||
if (!firstUuid) return;
|
||||
this.addSkillIcon(firstUuid, 1, !active, true, SkillTriggerType.Field, true, false, active ? undefined : pick.lv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 普攻 lv2+ 强化档作为独立技能图标
|
||||
*/
|
||||
private addNormalLv2Icon(normal: HSkillInfo | undefined, lv: number) {
|
||||
const entry = normal?.lv_entries?.[0];
|
||||
if (!normal || !entry) return;
|
||||
this.addSkillIcon(normal.uuid, entry.s_lv ?? 2, lv < entry.lv, false, undefined, false, true, entry.lv);
|
||||
const icon = this.skills_node.children[this.skills_node.children.length - 1];
|
||||
if (icon) icon.name = `skill_${normal.uuid}_lv2`;
|
||||
}
|
||||
|
||||
// ======================== 英雄动画 ========================
|
||||
|
||||
private updateHeroAnimation(uuid: number) {
|
||||
if (!this.hero_icon) return;
|
||||
const hero = HeroInfo[uuid];
|
||||
if (!hero) return;
|
||||
|
||||
const sprite = this.hero_icon.getComponent(Sprite) || this.hero_icon.getComponentInChildren(Sprite);
|
||||
if (sprite) sprite.spriteFrame = null;
|
||||
|
||||
const anim = this.hero_icon.getComponent(Animation) || this.hero_icon.addComponent(Animation);
|
||||
this.clearAnimationClips(anim);
|
||||
|
||||
this.iconVisualToken += 1;
|
||||
const token = this.iconVisualToken;
|
||||
const path = `game/heros/hero/${hero.path}/idle`;
|
||||
|
||||
resources.load(path, AnimationClip, (err, clip) => {
|
||||
if (err || !clip) {
|
||||
mLogger.log(this.debugMode, "HeroInfoComp", `load hero animation failed ${uuid}`, err);
|
||||
return;
|
||||
}
|
||||
if (token !== this.iconVisualToken) return;
|
||||
this.clearAnimationClips(anim);
|
||||
anim.addClip(clip);
|
||||
anim.play("idle");
|
||||
});
|
||||
}
|
||||
|
||||
private clearAnimationClips(anim: Animation) {
|
||||
const clips = anim.clips;
|
||||
if (clips && clips.length > 0) {
|
||||
for (let i = clips.length - 1; i >= 0; i--) {
|
||||
const clip = clips[i];
|
||||
if (clip) anim.removeClip(clip, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== UI 工具 ========================
|
||||
|
||||
private setLabelText(node: Node, text: string) {
|
||||
if (!node) return;
|
||||
const label = node.getComponent(Label) || node.getComponentInChildren(Label);
|
||||
if (label) {
|
||||
label.string = text;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.node.destroy();
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/HeroInfoComp.ts.meta
Normal file
9
assets/script/game/map/HeroInfoComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "16f4e273-a558-4bbe-8d0d-2c0210d9e9fe",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,38 +1,32 @@
|
||||
/**
|
||||
* @file HerosListComp.ts
|
||||
* @description 英雄图鉴弹出页面(UI 视图层)
|
||||
* @description 英雄图鉴页面(UI 视图层)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 以卡片列表形式展示所有可用英雄(使用 CardLiteComp 预制体)。
|
||||
* 2. 点击卡片选中英雄,右侧详情面板显示 idle 动画、名称、AP、HP、CD、技能描述。
|
||||
* 3. 支持卡池等级筛选(全部 / Lv1 / Lv2 / Lv3)。
|
||||
* 2. 点击卡片打开英雄详情弹窗(UIID.HeroInfo),详情/升级在 HeroInfoComp 中处理。
|
||||
* 3. 卡片排序:仅按品质降序 → uuid 升序(与养成进度解耦,保持展示稳定)。
|
||||
*
|
||||
* 关键设计:
|
||||
* - cards_node 为卡片容器,通过 instantiate(card_lite_prefab) 动态生成卡片。
|
||||
* - 选中状态通过 selectNode 高亮管理,同一时间只有一张卡片高亮。
|
||||
* - hero_icon 使用 Animation + iconVisualToken 机制防止异步加载竞态。
|
||||
* - 升级/碎片变动后通过 HERO_GROWTH_UPDATE 事件统一刷新所有卡片的可升级标识。
|
||||
* - 详情面板已从本组件剥离,由 HeroInfoComp 承载。
|
||||
* - 排序不依赖 smc,仅读取静态配置(HeroInfo.rarity)。
|
||||
*
|
||||
* 依赖:
|
||||
* - HeroInfo / HeroList(heroSet)—— 英雄静态配置与全量 UUID 列表
|
||||
* - CardLiteComp —— 轻量卡片组件
|
||||
* - buildHeroSkillDesc(HeroSkillText)—— 技能描述生成器(局外等级口径)
|
||||
* - UIID.HeroInfo(oops.gui)—— 英雄详情弹窗
|
||||
*/
|
||||
import { _decorator, Animation, AnimationClip, Button, Label, Node, Prefab, RichText, Sprite, UITransform, Vec3, Widget, instantiate, resources, tween, Tween } from "cc";
|
||||
import { _decorator, Node, Prefab, UITransform, instantiate } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { HeroInfo, HeroList, SkillTriggerType, LvFieldEntry, LvTriggerEntry, TriggerGrouped, HSkillInfo, getHeroDisplayStats, LvBonusAttrsEntry } from "../common/config/heroSet";
|
||||
import { buildHeroSkillDesc } from "../common/HeroSkillText";
|
||||
import { outlineRich } from "../common/RichOutline";
|
||||
import { HeroInfo, HeroList } from "../common/config/heroSet";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { HERO_UPGRADE_NEED_FRAGMENTS, RARITY_BG_COLORS } from "../common/config/GameSet";
|
||||
import { canHeroUpgrade } from "../common/config/heroUpSet";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { CardLiteComp } from "./CardLiteComp";
|
||||
import { ChipComp } from "./ChipComp";
|
||||
import { SmallTipComp } from "./SmallTipComp";
|
||||
import { SSkillBoxComp } from "./SSkillBoxComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
|
||||
const { property, ccclass } = _decorator;
|
||||
|
||||
@@ -41,149 +35,53 @@ const { property, ccclass } = _decorator;
|
||||
export class HerosListComp extends CCComp {
|
||||
// ======================== 编辑器绑定节点 ========================
|
||||
|
||||
@property(Node)
|
||||
hero_icon = null!
|
||||
|
||||
@property(Node)
|
||||
ap_node = null!
|
||||
|
||||
@property(Node)
|
||||
def_node = null!
|
||||
|
||||
@property(Node)
|
||||
hp_node = null!
|
||||
|
||||
@property(Node)
|
||||
cd_node = null!
|
||||
|
||||
/** 卡牌背景底框节点(按卡池等级切换子节点显示) */
|
||||
@property(Node)
|
||||
BG_node = null!
|
||||
|
||||
@property(Node)
|
||||
info_node = null!
|
||||
|
||||
@property(Node)
|
||||
name_node = null!
|
||||
|
||||
@property(Node)
|
||||
cards_node = null!
|
||||
|
||||
@property(Prefab)
|
||||
card_lite_prefab = null!
|
||||
|
||||
/** 卡池等级 Label,直接显示 X */
|
||||
@property(Label)
|
||||
lv_label = null!
|
||||
|
||||
/** 职业展示节点(子节点按 HRole 枚举值命名 0~5:0=坦克 1=战士 2=刺客 3=射手 4=法师 5=辅助) */
|
||||
@property(Node)
|
||||
role_node = null!
|
||||
|
||||
/** 升级按钮节点(smalltip 气泡已作为其子节点挂在下面) */
|
||||
@property(Node)
|
||||
upgrade_btn = null!
|
||||
|
||||
/** 碎片数量显示节点(Label) */
|
||||
@property(Node)
|
||||
fragment_node = null!
|
||||
|
||||
/** 碎片图标节点(挂载 ChipComp 用于渲染碎片图标) */
|
||||
@property(Node)
|
||||
chip_node = null!
|
||||
|
||||
/** 技能描述节点() */
|
||||
@property(Node)
|
||||
infos_node = null!
|
||||
|
||||
/** 技能图标容器节点(动态挂载 SSkillBoxComp 实例,展示当前英雄技能) */
|
||||
@property(Node)
|
||||
skills_node = null!
|
||||
|
||||
/** 技能小图标预制体(根节点挂 SSkillBoxComp) */
|
||||
@property(Prefab)
|
||||
skill_icon_prefab = null!
|
||||
/** 升级特效动画节点(内含升级效果 Animation,点击升级时从头播放) */
|
||||
@property(Node)
|
||||
upgrade_anim_node = null!
|
||||
|
||||
/** 碎片需求/总量 Label(显示 当前碎片 / 下级需求) */
|
||||
@property(Label)
|
||||
fragment_label = null!
|
||||
|
||||
/** 升级提升加成说明 RichText(展示升级后的属性提升,数值绿色高亮) */
|
||||
@property(RichText)
|
||||
upgrade_info_label = null!
|
||||
|
||||
/** 卡片宽度 */
|
||||
@property
|
||||
card_w = 120
|
||||
|
||||
/** 卡片高度 */
|
||||
@property
|
||||
card_h = 200
|
||||
|
||||
/** 卡片横向间隔 */
|
||||
@property
|
||||
spacing_x = 10
|
||||
|
||||
/** 卡片纵向间隔 */
|
||||
@property
|
||||
spacing_y = 10
|
||||
|
||||
/** 每行卡片数(竖向滚动时按列排列) */
|
||||
@property
|
||||
cols = 4
|
||||
|
||||
/** 卡片左右边距 */
|
||||
@property
|
||||
margin_x = 15
|
||||
|
||||
/** 卡片上下边距 */
|
||||
@property
|
||||
margin_y = 50
|
||||
|
||||
// ======================== 运行时状态 ========================
|
||||
|
||||
huuid: number = 0
|
||||
private iconVisualToken: number = 0
|
||||
private selectNode: Node | null = null
|
||||
debugMode: boolean = false
|
||||
|
||||
start() {
|
||||
this.bindUpgradeBtn()
|
||||
this.initCardList()
|
||||
// 升级特效动画初始隐藏,点击升级时才显示播放
|
||||
if (this.upgrade_anim_node) this.upgrade_anim_node.active = false
|
||||
if (HeroList.length > 0) {
|
||||
this.onCardSelect(HeroList[0])
|
||||
}
|
||||
}
|
||||
|
||||
protected onLoad(): void {
|
||||
// onLoad 阶段属性可能尚未绑定,延迟到 start 再处理
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定升级按钮点击事件
|
||||
* 在 start 中调用,确保 @property 属性已正确赋值
|
||||
*/
|
||||
private bindUpgradeBtn() {
|
||||
mLogger.log(this.debugMode, "HerosListComp", "bindUpgradeBtn", {
|
||||
hasUpgradeBtn: !!this.upgrade_btn,
|
||||
upgradeBtnName: this.upgrade_btn?.name,
|
||||
upgradeBtnActive: this.upgrade_btn?.active,
|
||||
upgradeBtnValid: this.upgrade_btn?.isValid,
|
||||
})
|
||||
|
||||
if (!this.upgrade_btn) {
|
||||
mLogger.error(this.debugMode, "HerosListComp", "upgrade_btn 未绑定,请检查编辑器属性面板")
|
||||
return
|
||||
}
|
||||
|
||||
// 确保 smalltip 气泡初始隐藏,避免遮挡按钮触摸(组件缺失时兜底补挂)
|
||||
const tipNode = this.upgrade_btn.getChildByName("smalltip")
|
||||
if (tipNode) {
|
||||
tipNode.getComponent(SmallTipComp) ?? tipNode.addComponent(SmallTipComp)
|
||||
tipNode.active = false
|
||||
}
|
||||
|
||||
let btn = this.upgrade_btn.getComponent(Button)
|
||||
if (!btn) btn = this.upgrade_btn.addComponent(Button)
|
||||
btn.interactable = true
|
||||
this.upgrade_btn.on(Button.EventType.CLICK, this.onUpgradeClick, this)
|
||||
mLogger.log(this.debugMode, "HerosListComp", "升级按钮点击事件已绑定")
|
||||
}
|
||||
|
||||
protected onEnable(): void {
|
||||
oops.message.on(GameEvent.HERO_GROWTH_UPDATE, this.onGrowthUpdate, this)
|
||||
if (this.cards_node && this.cards_node.children.length > 0) {
|
||||
this.onCardSelect(this.huuid || HeroList[0])
|
||||
}
|
||||
// 页面隐藏期间会错过事件(战斗结算发碎片时本节点 active=false 已注销监听),
|
||||
// 重新显示时必须补刷;首次 onEnable 早于 start,卡片未创建时 forEach 空数组安全
|
||||
this.refreshAllCardsCanUp()
|
||||
}
|
||||
|
||||
protected onDisable(): void {
|
||||
@@ -191,13 +89,10 @@ export class HerosListComp extends CCComp {
|
||||
}
|
||||
|
||||
private onGrowthUpdate() {
|
||||
if (this.huuid) {
|
||||
this.updateHeroDetail(this.huuid)
|
||||
}
|
||||
this.refreshAllCardsCanUp()
|
||||
}
|
||||
|
||||
/** 升级/碎片变动后统一刷新所有卡片的可升级标识与等级数字(升级入口仅在本面板,无需卡片各自监听事件) */
|
||||
/** 升级/碎片变动后统一刷新所有卡片的可升级标识与等级数字 */
|
||||
private refreshAllCardsCanUp() {
|
||||
if (!this.cards_node) return
|
||||
this.cards_node.children.forEach(cardNode => {
|
||||
@@ -211,143 +106,22 @@ export class HerosListComp extends CCComp {
|
||||
this.node.active = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级按钮点击回调
|
||||
* 消耗碎片提升英雄局外等级,成功后刷新详情面板
|
||||
*/
|
||||
onUpgradeClick() {
|
||||
mLogger.log(this.debugMode, "HerosListComp", "onUpgradeClick 被触发", { huuid: this.huuid })
|
||||
|
||||
const uuid = this.huuid
|
||||
if (!uuid) return
|
||||
|
||||
// 点击动画:按压回弹
|
||||
this.playUpgradeBtnAnim()
|
||||
|
||||
const success = smc.upgradeHero(uuid)
|
||||
if (success) {
|
||||
mLogger.log(this.debugMode, "HerosListComp", `英雄 ${uuid} 升级成功`)
|
||||
this.playUpgradeAnim()
|
||||
this.updateHeroDetail(uuid)
|
||||
} else {
|
||||
const growth = smc.getHeroGrowth(uuid)
|
||||
const need = HERO_UPGRADE_NEED_FRAGMENTS[growth.lv + 1]
|
||||
mLogger.log(this.debugMode, "HerosListComp", `英雄 ${uuid} 升级失败`, {
|
||||
currentLv: growth.lv,
|
||||
fragments: growth.fragments,
|
||||
needFragments: need ?? "MAX",
|
||||
})
|
||||
// 碎片不足 / 已满级:气泡提示
|
||||
const tipText = need === undefined ? "已满级" : "碎片不足"
|
||||
this.showSmallTip(tipText)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放升级按钮按压回弹动画
|
||||
*/
|
||||
private playUpgradeBtnAnim() {
|
||||
if (!this.upgrade_btn) {
|
||||
mLogger.log(this.debugMode, "HerosListComp", "playUpgradeBtnAnim: upgrade_btn 为空")
|
||||
return
|
||||
}
|
||||
mLogger.log(this.debugMode, "HerosListComp", "播放按钮动画")
|
||||
Tween.stopAllByTarget(this.upgrade_btn)
|
||||
this.upgrade_btn.setScale(1, 1, 1)
|
||||
tween(this.upgrade_btn)
|
||||
.to(0.08, { scale: new Vec3(0.9, 0.9, 1) }, { easing: 'quadOut' })
|
||||
.to(0.12, { scale: new Vec3(1.1, 1.1, 1) }, { easing: 'quadOut' })
|
||||
.to(0.08, { scale: new Vec3(1, 1, 1) }, { easing: 'quadIn' })
|
||||
.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放升级特效动画
|
||||
* 每次升级成功均从头播放;动画未播完时再次升级,立即重新开始
|
||||
*/
|
||||
private playUpgradeAnim() {
|
||||
if (!this.upgrade_anim_node) {
|
||||
mLogger.log(this.debugMode, "HerosListComp", "playUpgradeAnim: upgrade_anim_node 未绑定")
|
||||
return
|
||||
}
|
||||
const anim = this.upgrade_anim_node.getComponent(Animation)
|
||||
const clip = anim?.clips?.[0]
|
||||
if (!anim || !clip) {
|
||||
mLogger.log(this.debugMode, "HerosListComp", "playUpgradeAnim: 缺少 Animation 组件或动画剪辑")
|
||||
return
|
||||
}
|
||||
|
||||
this.upgrade_anim_node.active = true
|
||||
// 停止当前播放(如有),保证从头开始
|
||||
anim.stop()
|
||||
|
||||
const animState = anim.getState(clip.name)
|
||||
if (animState) {
|
||||
// 防止重复注册,播放结束后自动隐藏特效节点
|
||||
animState.off('finished', this.onUpgradeAnimFinished, this)
|
||||
animState.once('finished', this.onUpgradeAnimFinished, this)
|
||||
}
|
||||
anim.play(clip.name)
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级特效动画播放结束回调:隐藏特效节点
|
||||
*/
|
||||
private onUpgradeAnimFinished() {
|
||||
if (this.upgrade_anim_node && this.upgrade_anim_node.isValid) {
|
||||
this.upgrade_anim_node.active = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在升级按钮附近弹出 smalltip 气泡提示
|
||||
* @param text 提示文案
|
||||
*/
|
||||
private showSmallTip(text: string) {
|
||||
// smalltip 作为 upgrade_btn 的子节点挂在下面,统一走 SmallTipComp 播动画
|
||||
const tipNode = this.upgrade_btn?.getChildByName("smalltip")
|
||||
if (!tipNode || !tipNode.isValid) {
|
||||
mLogger.log(this.debugMode, "HerosListComp", `未在 upgrade_btn 下找到 smalltip 节点,无法提示:${text}`)
|
||||
return
|
||||
}
|
||||
const tip = tipNode.getComponent(SmallTipComp) ?? tipNode.addComponent(SmallTipComp)
|
||||
tip.show(text)
|
||||
}
|
||||
|
||||
// ======================== 卡片列表 ========================
|
||||
|
||||
private initCardList() {
|
||||
mLogger.log(this.debugMode, "HerosListComp", "initCardList start", {
|
||||
hasCardsNode: !!this.cards_node,
|
||||
hasPrefab: !!this.card_lite_prefab,
|
||||
heroListLen: HeroList.length,
|
||||
})
|
||||
|
||||
if (!this.cards_node || !this.card_lite_prefab) return
|
||||
|
||||
mLogger.log(this.debugMode, "HerosListComp", "cards_node info", {
|
||||
name: this.cards_node.name,
|
||||
active: this.cards_node.active,
|
||||
childrenCount: this.cards_node.children.length,
|
||||
pos: this.cards_node.position.toString(),
|
||||
scale: this.cards_node.scale.toString(),
|
||||
parentName: this.cards_node.parent?.name || "none",
|
||||
})
|
||||
|
||||
this.cards_node.removeAllChildren()
|
||||
|
||||
// 图鉴排序:按职业(HRole)升序 → uuid 升序(与养成进度解耦,保持展示稳定)
|
||||
const sorted = [...HeroList].sort((a, b) => {
|
||||
const ga = smc.getHeroGrowth(a)
|
||||
const gb = smc.getHeroGrowth(b)
|
||||
// 可升级的一批排在最左
|
||||
const ca = canHeroUpgrade(ga.lv, ga.fragments) ? 0 : 1
|
||||
const cb = canHeroUpgrade(gb.lv, gb.fragments) ? 0 : 1
|
||||
if (ca !== cb) return ca - cb
|
||||
// 同批内:现有等级降序优先
|
||||
if (ga.lv !== gb.lv) return gb.lv - ga.lv
|
||||
// 等级相同:品质降序(rarity 越大品质越高)
|
||||
return (HeroInfo[b]?.rarity ?? 1) - (HeroInfo[a]?.rarity ?? 1)
|
||||
});
|
||||
const heroA = HeroInfo[a];
|
||||
const heroB = HeroInfo[b];
|
||||
const roleA = heroA?.role ?? 0;
|
||||
const roleB = heroB?.role ?? 0;
|
||||
if (roleA !== roleB) return roleA - roleB;
|
||||
return a - b;
|
||||
})
|
||||
|
||||
for (const uuid of sorted) {
|
||||
const hero = HeroInfo[uuid]
|
||||
@@ -356,20 +130,12 @@ export class HerosListComp extends CCComp {
|
||||
const cardNode = instantiate(this.card_lite_prefab)
|
||||
cardNode.name = `card_${uuid}`
|
||||
|
||||
mLogger.log(this.debugMode, "HerosListComp", `card instantiated ${uuid}`, {
|
||||
cardActive: cardNode.active,
|
||||
cardPos: cardNode.position.toString(),
|
||||
cardScale: cardNode.scale.toString(),
|
||||
cardChildren: cardNode.children.map(c => c.name),
|
||||
cardWidth: cardNode.getComponent("cc.UITransform")?.width,
|
||||
cardHeight: cardNode.getComponent("cc.UITransform")?.height,
|
||||
})
|
||||
|
||||
const comp = cardNode.getComponent(CardLiteComp) || cardNode.addComponent(CardLiteComp)
|
||||
comp.setHeroByUuid(uuid)
|
||||
comp.onClickCallback = (cardComp: CardLiteComp) => {
|
||||
this.onCardSelect(uuid)
|
||||
this.highlightCard(cardNode)
|
||||
comp.onClickCallback = () => {
|
||||
// 点击卡片:打开英雄详情弹窗(详情/升级/前后切换由 HeroInfoComp 承载)
|
||||
oops.audio.playEffect("music/button");
|
||||
oops.gui.open(UIID.HeroInfo, { uuid })
|
||||
}
|
||||
|
||||
this.cards_node.addChild(cardNode)
|
||||
@@ -386,389 +152,23 @@ export class HerosListComp extends CCComp {
|
||||
const total = this.cards_node.children.length
|
||||
if (total === 0) return
|
||||
|
||||
// 宽度 = 卡片总宽 + 卡片间隔 + 左右边距
|
||||
const contentW = total * this.card_w
|
||||
+ (total - 1) * this.spacing_x
|
||||
// 竖向滚动:宽度固定(每行 cols 列),高度按行数累计
|
||||
const rows = Math.ceil(total / this.cols)
|
||||
|
||||
// 宽度 = 每行卡片总宽 + 横向间隔 + 左右边距
|
||||
const contentW = this.cols * this.card_w
|
||||
+ (this.cols - 1) * this.spacing_x
|
||||
+ this.margin_x * 2
|
||||
|
||||
const cols = 4
|
||||
const rows = Math.ceil(total / cols)
|
||||
const cardH = 200
|
||||
const spacingY = 10
|
||||
const contentH = Math.max(1000, rows * (cardH + spacingY) + 50)
|
||||
// 高度 = 行数 × 卡片高 + 纵向间隔 + 上下边距
|
||||
const contentH = rows * this.card_h
|
||||
+ (rows - 1) * this.spacing_y
|
||||
+ this.margin_y * 2
|
||||
|
||||
const uiTrans = this.cards_node.getComponent(UITransform)
|
||||
if (uiTrans) {
|
||||
uiTrans.setContentSize(contentW, contentH)
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, "HerosListComp", "updateContentSize", {
|
||||
total, cols, rows, contentW, contentH,
|
||||
})
|
||||
}
|
||||
|
||||
private highlightCard(cardNode: Node) {
|
||||
// 取消旧选中:关闭其 active 标识节点
|
||||
if (this.selectNode) {
|
||||
this.selectNode.getComponent(CardLiteComp)?.setSelected(false)
|
||||
}
|
||||
this.selectNode = cardNode
|
||||
// 新选中:激活根节点下的 active 标识节点(替代原缩放高亮)
|
||||
cardNode.getComponent(CardLiteComp)?.setSelected(true)
|
||||
}
|
||||
|
||||
private onCardSelect(uuid: number) {
|
||||
this.huuid = uuid
|
||||
this.updateHeroDetail(uuid)
|
||||
}
|
||||
|
||||
// ======================== 详情面板 ========================
|
||||
|
||||
private updateHeroDetail(uuid: number) {
|
||||
const hero = HeroInfo[uuid]
|
||||
if (!hero) return
|
||||
|
||||
// 属性按当前局外等级计算(与技能面板 updateSkillIcons 同源,均为 getHeroOuterLv)
|
||||
const heroLv = Math.max(1, smc.getHeroOuterLv(uuid))
|
||||
this.setLabelText(this.name_node, hero.name || "")
|
||||
// 属性面板与运行时口径一致:基础值 × 品质系数 + 职业成长 + bonus + lv12 专项
|
||||
const stats = getHeroDisplayStats(hero, heroLv)
|
||||
this.setLabelText(this.ap_node, `${stats.ap}`)
|
||||
this.setLabelText(this.def_node, `${stats.def}`)
|
||||
this.setLabelText(this.hp_node, `${stats.hp}`)
|
||||
|
||||
this.updateCdDisplay(hero)
|
||||
|
||||
if (this.info_node) {
|
||||
// Label 承载:HeroSkillText 输出富文本,剥离 BBCode 标签转纯文本
|
||||
const desc = buildHeroSkillDesc(hero.uuid, true, true).replace(/<[^>]+>/g, "")
|
||||
const infoLabel = this.info_node.getChildByName("info")?.getComponent(Label)
|
||||
|| this.info_node.getComponent(Label)
|
||||
|| this.info_node.getComponentInChildren(Label)
|
||||
if (infoLabel) infoLabel.string = desc || hero.info || ""
|
||||
}
|
||||
|
||||
this.updatePoolLvBg(hero.rarity ?? 1)
|
||||
this.updateLvDisplay(uuid)
|
||||
this.updateRoleDisplay(hero)
|
||||
this.updateHeroAnimation(uuid)
|
||||
this.updateGrowthPanel(uuid)
|
||||
this.updateSkillIcons(hero)
|
||||
}
|
||||
|
||||
private updatePoolLvBg(rarity: number) {
|
||||
if (!this.BG_node) return
|
||||
// BG_node 下直接放置颜色节点(green/blue/purple/yellow/red),按卡池等级激活对应颜色
|
||||
const color = RARITY_BG_COLORS[Math.min(rarity, 5) - 1]
|
||||
this.BG_node.children.forEach(child => {
|
||||
child.active = (child.name === color)
|
||||
})
|
||||
}
|
||||
|
||||
private updateCdDisplay(hero: typeof HeroInfo[number]) {
|
||||
if (!this.cd_node) return
|
||||
const skillKeys = Object.keys(hero.skills)
|
||||
if (skillKeys.length === 0) return
|
||||
const firstSkill = hero.skills[Number(skillKeys[0])]
|
||||
if (firstSkill) {
|
||||
this.setLabelText(this.cd_node, `${firstSkill.cd}s`)
|
||||
}
|
||||
}
|
||||
|
||||
private updateLvDisplay(uuid: number) {
|
||||
if (!this.lv_label) return
|
||||
const outerLv = smc.getHeroOuterLv(uuid)
|
||||
this.lv_label.string = `${outerLv}`
|
||||
}
|
||||
|
||||
private updateRoleDisplay(hero: typeof HeroInfo[number]) {
|
||||
if (!this.role_node) return
|
||||
this.role_node.active = true
|
||||
const roleStr = `${hero.role ?? 0}`
|
||||
this.role_node.children.forEach(child => {
|
||||
child.active = (child.name === roleStr)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新局外成长面板:碎片数量与升级按钮状态
|
||||
* @param uuid 英雄模板 uuid
|
||||
*/
|
||||
private updateGrowthPanel(uuid: number) {
|
||||
const growth = smc.getHeroGrowth(uuid)
|
||||
const needFragments = HERO_UPGRADE_NEED_FRAGMENTS[growth.lv + 1]
|
||||
const isMaxLv = needFragments === undefined
|
||||
|
||||
// 碎片数量显示:当前拥有 / 下一级所需(满级时显示 MAX)
|
||||
if (this.fragment_node) {
|
||||
const fragmentText = isMaxLv
|
||||
? `${growth.fragments} / MAX`
|
||||
: `${growth.fragments} / ${needFragments}`
|
||||
this.setLabelText(this.fragment_node, fragmentText)
|
||||
}
|
||||
// 独立碎片 Label(编辑器可直接绑定 Label 组件,与 fragment_node 二选一或并存)
|
||||
if (this.fragment_label) {
|
||||
const fragmentText = isMaxLv
|
||||
? `${growth.fragments} / MAX`
|
||||
: `${growth.fragments} / ${needFragments}`
|
||||
this.fragment_label.string = fragmentText
|
||||
}
|
||||
|
||||
// 升级提升加成说明
|
||||
this.updateUpgradeInfoLabel(uuid, growth.lv, isMaxLv)
|
||||
|
||||
// 碎片图标:使用 ChipComp 渲染英雄图标(自动读取 HeroInfo 配置)
|
||||
if (this.chip_node) {
|
||||
const chipComp = this.chip_node.getComponent(ChipComp) || this.chip_node.addComponent(ChipComp)
|
||||
chipComp.setHeroByUuid(uuid)
|
||||
}
|
||||
|
||||
// 升级按钮状态:始终可点击,点击后由 onUpgradeClick 判断碎片是否足够
|
||||
// 碎片不足时按钮保持可点,通过气泡提示用户(而非禁用按钮)
|
||||
if (this.upgrade_btn) {
|
||||
this.upgrade_btn.active = true
|
||||
const btnComp = this.upgrade_btn.getComponent("cc.Button") as import("cc").Button | null
|
||||
if (btnComp) btnComp.interactable = true
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 技能图标列表 ========================
|
||||
|
||||
/**
|
||||
* 刷新升级提升加成说明 RichText
|
||||
* 展示口径:对比当前等级与下一等级,仅列出 AP/HP/DEF 属性差值,单行展示
|
||||
* 数值用绿色高亮,整体通过 outlineRich 包裹描边标签
|
||||
* @param uuid 英雄模板 uuid
|
||||
* @param curLv 当前局外等级(≥1)
|
||||
* @param isMaxLv 是否已满级
|
||||
*/
|
||||
private updateUpgradeInfoLabel(uuid: number, curLv: number, isMaxLv: boolean) {
|
||||
if (!this.upgrade_info_label) return
|
||||
const hero = HeroInfo[uuid]
|
||||
if (!hero) { this.upgrade_info_label.string = ""; return }
|
||||
|
||||
if (isMaxLv) {
|
||||
this.upgrade_info_label.string = outlineRich("已满级")
|
||||
return
|
||||
}
|
||||
|
||||
const nextLv = curLv + 1
|
||||
const cur = getHeroDisplayStats(hero, curLv)
|
||||
const nxt = getHeroDisplayStats(hero, nextLv)
|
||||
|
||||
// 属性提升差值:单行展示 AP/HP/DEF,数值绿色高亮
|
||||
const parts: string[] = []
|
||||
if (nxt.hp > cur.hp) parts.push(`生命<color=#00FF00>+${nxt.hp - cur.hp}</color>`)
|
||||
if (nxt.ap > cur.ap) parts.push(`攻击<color=#00FF00>+${nxt.ap - cur.ap}</color>`)
|
||||
if (nxt.def > cur.def) parts.push(`防御<color=#00FF00>+${nxt.def - cur.def}</color>`)
|
||||
|
||||
this.upgrade_info_label.string = parts.length > 0
|
||||
? outlineRich(`升级属性提升:${parts.join(" ")}`)
|
||||
: outlineRich(`升级至 Lv.${nextLv}`)
|
||||
}
|
||||
|
||||
/** 图鉴面板展示的触发技能槽位(按触发时机排序:召唤→回合开始→攻击后→受击后→死亡→回合结束→常驻光环) */
|
||||
private static readonly PANEL_TRIGGER_TYPES: SkillTriggerType[] = [
|
||||
SkillTriggerType.Call,
|
||||
SkillTriggerType.FStart,
|
||||
SkillTriggerType.Atking,
|
||||
SkillTriggerType.Atked,
|
||||
SkillTriggerType.Dead,
|
||||
SkillTriggerType.FEnd,
|
||||
SkillTriggerType.Field,
|
||||
]
|
||||
|
||||
/**
|
||||
* 刷新当前英雄的技能图标列表:
|
||||
* 1. 大招:skills[1] 及以后(首个为普攻,跳过)。
|
||||
* 2. 触发技能:每个触发槽位(Call/FStart/Atking/Atked/Dead/FEnd/Field)整组视为一个技能,
|
||||
* 只出一个图标,组内多技能/词条合并展示(复活已纳入 dead)。
|
||||
* 以局外等级(getHeroOuterLv)为当前等级,未达解锁档的显示 locked,
|
||||
* 已激活的显示当前等级对应的技能等级。
|
||||
*/
|
||||
private updateSkillIcons(hero: typeof HeroInfo[number]) {
|
||||
if (!this.skills_node || !this.skill_icon_prefab) return
|
||||
|
||||
// 清空旧图标(显式销毁,避免节点泄漏)
|
||||
for (let i = this.skills_node.children.length - 1; i >= 0; i--) {
|
||||
this.skills_node.children[i].destroy()
|
||||
}
|
||||
|
||||
// huuid 已在 onCardSelect 赋值,addSkillIcon 用它携带英雄语境
|
||||
const lv = Math.max(1, smc.getHeroOuterLv(hero.uuid))
|
||||
|
||||
// ---- 大招:skills[1] 及以后(英雄出场即激活,与能量条同语义,恒不锁定) ----
|
||||
Object.values(hero.skills).slice(1).forEach(hSkill => {
|
||||
// 技能等级展示与 HeroSkillText 取档同规则:lv_entries 取 ≤lv 最大档的 s_lv,无生效档回退配置 lv
|
||||
const sLv = hSkill.lv_entries?.length
|
||||
? (hSkill.lv_entries.filter(e => e.lv <= lv).pop()?.s_lv ?? hSkill.lv)
|
||||
: hSkill.lv
|
||||
this.addSkillIcon(hSkill.uuid, sLv, false, false)
|
||||
})
|
||||
|
||||
// ---- 普攻 lv2 强化档:作为独立技能图标(名称/图标取 lv1,弹窗只展示 lv2 说明) ----
|
||||
this.addNormalLv2Icon(Object.values(hero.skills)[0], lv)
|
||||
|
||||
// ---- 触发技能:每个触发槽位整组一个图标,按 PANEL_TRIGGER_TYPES 顺序追加 ----
|
||||
for (const type of HerosListComp.PANEL_TRIGGER_TYPES) {
|
||||
if (type === SkillTriggerType.Field) {
|
||||
this.addFieldGroupIcon(hero[SkillTriggerType.Field], lv)
|
||||
} else {
|
||||
this.addTriggerGroupIcon(hero[type] as TriggerGrouped | undefined, lv, type)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- bonus 档位:配置了 icon 的档位在技能列表最末各出一个图标 ----
|
||||
this.addBonusIcons(hero.bonus, lv)
|
||||
}
|
||||
|
||||
/**
|
||||
* bonus 档位图标:配置了 icon 的档位各出一个图标(追加在技能列表最末)。
|
||||
* 激活判定:英雄局外等级 ≥ 档位 lv;未激活显示锁定遮罩。
|
||||
* 点击弹窗走 SinfoComp bonus 专属渲染(名称/图标取档位配置,主说明为 attrs 效果)。
|
||||
*/
|
||||
private addBonusIcons(bonus: LvBonusAttrsEntry[] | undefined, lv: number) {
|
||||
if (!bonus?.length) return
|
||||
for (const entry of bonus) {
|
||||
if (!entry.icon) continue
|
||||
const node = instantiate(this.skill_icon_prefab)
|
||||
node.name = `bonus_lv${entry.lv}`
|
||||
const comp = node.getComponent(SSkillBoxComp) || node.addComponent(SSkillBoxComp)
|
||||
comp.initBonus(this.huuid, entry, lv < entry.lv)
|
||||
this.skills_node.addChild(node)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化技能小图标并初始化
|
||||
* @param s_uuid 技能 UUID(SkillSet 或 FieldSkillSet;整组模式下取组内首个,仅用于图标)
|
||||
* @param s_lv 当前等级对应的技能等级(locked 时为解锁预览档)
|
||||
* @param locked 当前等级未激活 → 显示锁定遮罩
|
||||
* @param isField true 表示驻场光环词条(图标取 FieldSkillSet)
|
||||
* @param slot 技能所属槽位(普攻/大招传 undefined;触发/光环传对应类型,
|
||||
* 同一底座不同槽位视为不同技能,弹窗按槽位取档;复活已纳入 dead)
|
||||
* @param isGroup 整组模式:该图标代表整个触发槽位,组名取组内首个自定义名,
|
||||
* 弹窗按整组合并渲染(组内多技能/词条统一说明)
|
||||
* @param normalLv2 普攻 lv2 专属模式:该图标代表普攻 lv2 强化档,名称/图标取 lv1,
|
||||
* 弹窗只展示 lv2 档位说明
|
||||
* @param unlockLv 未激活时的解锁所需英雄等级(用于"x级解锁"展示,与技能等级 s_lv 解耦)
|
||||
*/
|
||||
private addSkillIcon(s_uuid: number, s_lv: number, locked: boolean, isField: boolean, slot?: SkillTriggerType, isGroup: boolean = false, normalLv2: boolean = false, unlockLv?: number) {
|
||||
const node = instantiate(this.skill_icon_prefab)
|
||||
node.name = `skill_${s_uuid}`
|
||||
const comp = node.getComponent(SSkillBoxComp) || node.addComponent(SSkillBoxComp)
|
||||
// 携带所属英雄与槽位,点击弹窗按英雄技能分档描述渲染
|
||||
comp.initHeroSkill(s_uuid, s_lv, locked, isField, this.huuid, slot, isGroup, normalLv2, unlockLv)
|
||||
this.skills_node.addChild(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发技能槽位(call/fstart/atking/atked/dead)整组一个图标:
|
||||
* 组内多 s_uuid 合并展示,图标/等级取组内首个 s_uuid 的生效档(未激活预览最低档);
|
||||
* 整组激活判定:组内任一 s_uuid 存在 ≤lv 生效档即视为已激活(与 resolveTriggerByLv 同语义)
|
||||
* @param type 触发槽位(随图标透传,弹窗按整组取档)
|
||||
*/
|
||||
private addTriggerGroupIcon(grouped: TriggerGrouped | undefined, lv: number, type: SkillTriggerType) {
|
||||
if (!grouped) return
|
||||
const keys = Object.keys(grouped).map(Number).filter(k => grouped[k]?.length)
|
||||
if (keys.length === 0) return
|
||||
// 图标/等级取组内首个 s_uuid 的生效档
|
||||
const sorted = [...grouped[keys[0]]].sort((a, b) => a.lv - b.lv)
|
||||
let active: LvTriggerEntry | undefined
|
||||
for (const e of sorted) { if (e.lv <= lv) active = e }
|
||||
// 整组激活判定:组内任一 s_uuid 存在生效档即激活
|
||||
let anyActive = !!active
|
||||
for (const key of keys) {
|
||||
if (anyActive) break
|
||||
for (const e of grouped[key] ?? []) {
|
||||
if (e.lv <= lv) { anyActive = true; break }
|
||||
}
|
||||
}
|
||||
const pick = active ?? sorted[0]
|
||||
this.addSkillIcon(keys[0], pick.s_lv ?? 1, !anyActive, false, type, true, false, anyActive ? undefined : pick.lv)
|
||||
}
|
||||
|
||||
/**
|
||||
* 驻场光环槽位整组一个图标:组内多词条合并展示,
|
||||
* 图标取生效档(未激活预览最低档)的首个词条
|
||||
*/
|
||||
private addFieldGroupIcon(field: LvFieldEntry[] | undefined, lv: number) {
|
||||
if (!field?.length) return
|
||||
const sorted = [...field].sort((a, b) => a.lv - b.lv)
|
||||
let active: LvFieldEntry | undefined
|
||||
for (const e of sorted) { if (e.lv <= lv) active = e }
|
||||
const pick = active ?? sorted[0]
|
||||
const firstUuid = pick.uuids[0]?.uuid
|
||||
if (!firstUuid) return
|
||||
// 词条档位无 s_lv 概念,技能等级恒为 1(等级标签展示技能等级而非解锁的英雄等级)
|
||||
this.addSkillIcon(firstUuid, 1, !active, true, SkillTriggerType.Field, true, false, active ? undefined : pick.lv)
|
||||
}
|
||||
|
||||
/** 复活技能已纳入 dead 触发组,通过 dead 配置展示(revive_hp / revive_count),无需单独处理 */
|
||||
|
||||
/**
|
||||
* 普攻 lv2+ 强化档作为独立技能图标:
|
||||
* 普攻(skills[0])lv_entries 存在档时单独出图标,名称/图标取 lv1(s_uuid 相同),
|
||||
* 等级显示偏移一档(配置 Lv2 → Lv1,SSkillBoxComp 统一处理),弹窗按 hero_normal_lv2
|
||||
* 模式只展示 lv2+ 档位说明;激活判定按档位的英雄等级(lv 阈值)。
|
||||
* Why: 普攻 lv2 档是独立成长机制(如附加暴击/冰冻),需让玩家明确感知其存在与解锁条件。
|
||||
*/
|
||||
private addNormalLv2Icon(normal: HSkillInfo | undefined, lv: number) {
|
||||
const entry = normal?.lv_entries?.[0]
|
||||
if (!normal || !entry) return
|
||||
// 节点命名区分 lv2 档(普攻与触发槽位可能同底座,uuid 重名会互相干扰)
|
||||
this.addSkillIcon(normal.uuid, entry.s_lv ?? 2, lv < entry.lv, false, undefined, false, true, entry.lv)
|
||||
const icon = this.skills_node.children[this.skills_node.children.length - 1]
|
||||
if (icon) icon.name = `skill_${normal.uuid}_lv2`
|
||||
}
|
||||
|
||||
// ======================== 英雄动画 ========================
|
||||
|
||||
private updateHeroAnimation(uuid: number) {
|
||||
if (!this.hero_icon) return
|
||||
const hero = HeroInfo[uuid]
|
||||
if (!hero) return
|
||||
|
||||
const sprite = this.hero_icon.getComponent(Sprite) || this.hero_icon.getComponentInChildren(Sprite)
|
||||
if (sprite) sprite.spriteFrame = null
|
||||
|
||||
const anim = this.hero_icon.getComponent(Animation) || this.hero_icon.addComponent(Animation)
|
||||
this.clearAnimationClips(anim)
|
||||
|
||||
this.iconVisualToken += 1
|
||||
const token = this.iconVisualToken
|
||||
const path = `game/heros/hero/${hero.path}/idle`
|
||||
|
||||
resources.load(path, AnimationClip, (err, clip) => {
|
||||
if (err || !clip) {
|
||||
mLogger.log(this.debugMode, "HerosListComp", `load hero animation failed ${uuid}`, err)
|
||||
return
|
||||
}
|
||||
if (token !== this.iconVisualToken) return
|
||||
this.clearAnimationClips(anim)
|
||||
anim.addClip(clip)
|
||||
anim.play("idle")
|
||||
})
|
||||
}
|
||||
|
||||
private clearAnimationClips(anim: Animation) {
|
||||
const clips = anim.clips
|
||||
if (clips && clips.length > 0) {
|
||||
for (let i = clips.length - 1; i >= 0; i--) {
|
||||
const clip = clips[i]
|
||||
if (clip) anim.removeClip(clip, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== UI 工具 ========================
|
||||
|
||||
private setLabelText(node: Node, text: string) {
|
||||
if (!node) return
|
||||
const label = node.getComponent(Label) || node.getComponentInChildren(Label)
|
||||
if (label) {
|
||||
label.string = text
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
@@ -54,7 +54,6 @@ import { oops } from "db://oops-framework/core/Oops";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { HeroInfo, HType, resolveTriggerByLv, resolveFieldByLv, resolveSkillOverrides, calcGrowBonus, getHeroQualityCoef } from "../common/config/heroSet";
|
||||
import { applyTalentBonuses } from "../common/config/TalentSet";
|
||||
import { HeroViewComp } from "../hero/HeroViewComp";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
import { MissionEconomy } from "./MissionEconomy";
|
||||
@@ -2094,13 +2093,16 @@ export class MissionCardComp extends CCComp {
|
||||
model.knockback_distance = 0;
|
||||
model.heal_power = 0;
|
||||
model.buff_power = 0;
|
||||
applyTalentBonuses(model, smc.getTalentLevels(), model.role);
|
||||
// 天赋触发修正归零(applyTalent 内部重算,保证升级重算幂等)
|
||||
model.talent_t_num_reduce = 0;
|
||||
model.talent_extra_trigger_chance = 0;
|
||||
model.applyTalent();
|
||||
}
|
||||
|
||||
// 血量按当前血线比例映射到新上限,保留升级前战斗状态
|
||||
model.hp = Math.max(1, Math.floor(model.hp_max * Math.max(0, Math.min(1, hpRate))));
|
||||
|
||||
// 重新解析触发技能/驻场光环/复活到运行时缓存(与 Hero.load 对齐)
|
||||
// 重新解析触发技能/驻场光环/复活到运行时缓存(在 applyTalent 之后,确保 L4/L5 触发修正已写入模型)
|
||||
model.runtime_call = resolveTriggerByLv(model.call, nextLv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, nextLv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, nextLv);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* - 第 5 节胜利 → open_Victory(final) 弹结算,确认退出并存档进下一关。
|
||||
* - cleanComponents() / clearBattlePools() 仅在整关开始/结束时执行,节间不调用。
|
||||
*/
|
||||
import { _decorator, Vec3, Animation, instantiate, Prefab, Node, NodeEventType, ProgressBar, Label, CCInteger, tween, v3, Tween, Widget, UIOpacity } from "cc";
|
||||
import { _decorator, Node, NodeEventType, Label, tween, v3, Tween, Widget, Color, Sprite, ProgressBar } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
@@ -31,7 +31,6 @@ import { SkillTriggerHelper } from "../hero/SkillTriggerHelper";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { SkillView } from "../skill/SkillView";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
import { HeroInfo } from "../common/config/heroSet";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { Monster } from "../hero/Mon";
|
||||
import { Skill } from "../skill/Skill";
|
||||
@@ -80,8 +79,6 @@ type SettleType = "defeat" | "final";
|
||||
export class MissionComp extends CCComp {
|
||||
@property({ tooltip: "是否启用调试日志" })
|
||||
private debugMode: boolean = true;
|
||||
@property({ tooltip: "是否显示战斗内存观测面板" })
|
||||
private showMemoryPanel: boolean = false;
|
||||
|
||||
|
||||
// ======================== 配置参数 ========================
|
||||
@@ -97,6 +94,21 @@ export class MissionComp extends CCComp {
|
||||
@property(Node)
|
||||
tooltip: Node = null!
|
||||
|
||||
/** 回合指示节点(5 个,索引 0~4 对应回合 1~5),按回合进度染色 */
|
||||
@property({ type: [Node], tooltip: "回合指示节点(按回合 1~5 顺序绑定)" })
|
||||
round_nodes: Node[] = [];
|
||||
|
||||
/** 回合进度条:第 1 回合 0%,每推进一回合 +25%(第 5 回合满格) */
|
||||
@property({ type: ProgressBar, tooltip: "回合进度条(第 1 回合 0%,每回合 +25%)" })
|
||||
round_progress: ProgressBar = null!;
|
||||
|
||||
/** 回合指示:未激活(白色) */
|
||||
private static readonly ROUND_INACTIVE = new Color(255, 255, 255, 255);
|
||||
/** 回合指示:已激活(橙黄 #FFA300) */
|
||||
private static readonly ROUND_ACTIVE = new Color(255, 163, 0, 255);
|
||||
/** 回合指示:Boss 当前回合(红色) */
|
||||
private static readonly ROUND_BOSS = new Color(230, 60, 60, 255);
|
||||
|
||||
/** 阶段名称映射表(用于 UI 显示) */
|
||||
private static readonly PhaseNameMap: Record<MissionPhase, string> = {
|
||||
[MissionPhase.None]: "未开始",
|
||||
@@ -133,26 +145,6 @@ export class MissionComp extends CCComp {
|
||||
private lastTimeStr: string = "";
|
||||
/** 上一次显示的秒数(避免重复计算) */
|
||||
private lastTimeSecond: number = -1;
|
||||
/** 性能监控面板 Label 引用 */
|
||||
private memoryLabel: Label | null = null;
|
||||
/** 性能监控刷新计时器 */
|
||||
private memoryRefreshTimer: number = 0;
|
||||
/** 上一次性能文本(避免重复渲染) */
|
||||
private lastMemoryText: string = "";
|
||||
/** 帧间隔累加(用于计算平均 FPS) */
|
||||
private perfDtAcc: number = 0;
|
||||
/** 帧数计数 */
|
||||
private perfFrameCount: number = 0;
|
||||
/** 初始堆内存基准值(MB) */
|
||||
private heapBaseMB: number = -1;
|
||||
/** 堆内存峰值(MB) */
|
||||
private heapPeakMB: number = 0;
|
||||
/** 堆内存增长趋势(MB/分钟) */
|
||||
private heapTrendPerMinMB: number = 0;
|
||||
/** 趋势计算计时器 */
|
||||
private heapTrendTimer: number = 0;
|
||||
/** 趋势计算基准(MB) */
|
||||
private heapTrendBaseMB: number = -1;
|
||||
/** 怪物数量同步计时器(降低同步频率) */
|
||||
private monsterCountSyncTimer: number = 0;
|
||||
/** 当前回合数(= 当前小节号,驱动 HUD 回合/进度显示) */
|
||||
@@ -161,8 +153,6 @@ export class MissionComp extends CCComp {
|
||||
private isBossWave: boolean = false;
|
||||
/** 超时回合已在 onBattleTimeout 预扣留存分,BattleEnd 需跳过重复累加 */
|
||||
private skipRemainScoreOnBattleEnd: boolean = false;
|
||||
/** 上节清场时间快照(含清场加速还原,供评分统计) */
|
||||
private lastWaveClearTime: number = 0;
|
||||
/** 当前任务阶段 */
|
||||
public currentPhase: MissionPhase = MissionPhase.None;
|
||||
/** 是否处于回合间倒计时状态 */
|
||||
@@ -180,11 +170,9 @@ export class MissionComp extends CCComp {
|
||||
|
||||
// ======================== 关卡进度状态 ========================
|
||||
|
||||
/** 当前章节号 */
|
||||
public currentChapter: number = 1;
|
||||
/** 当前关卡号(1~10) */
|
||||
/** 当前关卡号(无限关,1~∞) */
|
||||
public currentStage: number = 1;
|
||||
/** 当前小节号(1~SECTIONS_PER_STAGE) */
|
||||
/** 当前回合号(1~SECTIONS_PER_STAGE) */
|
||||
public currentSection: number = 1;
|
||||
|
||||
// ======================== ECS 查询匹配器(预缓存) ========================
|
||||
@@ -202,7 +190,6 @@ export class MissionComp extends CCComp {
|
||||
this.heroViewMatcher = ecs.allOf(HeroViewComp);
|
||||
this.skillViewMatcher = ecs.allOf(SkillView);
|
||||
this.heroAttrsMatcher = ecs.allOf(HeroAttrsComp);
|
||||
this.showMemoryPanel = false
|
||||
// 注册生命周期事件
|
||||
this.on(GameEvent.MissionEnd, this.mission_end, this)
|
||||
this.on(GameEvent.NewWave, this.onNewWave, this)
|
||||
@@ -212,10 +199,9 @@ export class MissionComp extends CCComp {
|
||||
this.start_btn?.on(NodeEventType.TOUCH_END, this.onStartFightBtnClick, this)
|
||||
// 开局免费三选一选完后自动进入战斗
|
||||
oops.message.on("OpeningPicksDone", this.onOpeningPicksDone, this);
|
||||
this.removeMemoryPanel()
|
||||
}
|
||||
|
||||
onAdded(args: any) {
|
||||
onAdded() {
|
||||
// 使用 scheduleOnce 将事件推迟到下一帧执行,
|
||||
// 确保所有关联组件(如 MissionCardComp 等)都已经完成其 onLoad 生命周期。
|
||||
this.scheduleOnce(() => {
|
||||
@@ -370,7 +356,6 @@ export class MissionComp extends CCComp {
|
||||
this.changePhase(MissionPhase.Prepare);
|
||||
// 派发首个 SectionStart,MissionMonComp 据此生成开局怪物
|
||||
oops.message.dispatchEvent(GameEvent.SectionStart, {
|
||||
chapter: this.currentChapter,
|
||||
stage: this.currentStage,
|
||||
section: this.currentSection,
|
||||
});
|
||||
@@ -455,7 +440,6 @@ export class MissionComp extends CCComp {
|
||||
*/
|
||||
private changePhase(targetPhase: MissionPhase) {
|
||||
if (this.currentPhase === targetPhase) return;
|
||||
const oldPhase = this.currentPhase;
|
||||
this.currentPhase = targetPhase;
|
||||
|
||||
const phaseName = MissionComp.PhaseNameMap[targetPhase] || "未知";
|
||||
@@ -556,8 +540,6 @@ export class MissionComp extends CCComp {
|
||||
// 英雄为真实死亡(实体已销毁),本节死亡数取死亡记录长度
|
||||
const heroDeathCount = smc.mission.dead_heroes.length;
|
||||
|
||||
// 快照清场时间供评分统计(onNewWave 会清零 clearTime,必须提前快照)
|
||||
this.lastWaveClearTime = this.clearTime;
|
||||
// 【评分系统 - 战绩分】记录全员存活的胜利节数(额外加分)
|
||||
if (hasHero && heroDeathCount === 0) {
|
||||
smc.vmdata.scores.wave_all_alive_count++;
|
||||
@@ -751,11 +733,11 @@ export class MissionComp extends CCComp {
|
||||
* - 胜利即 final(通关),存档推进到下一关;失败即 defeat(进度不变,可复活/再来一次)
|
||||
* - 切入 BattleEnd,2s 后由 autoNextPhase 自动弹出 VictoryComp
|
||||
*
|
||||
* @param e 事件对象(未使用)
|
||||
* @param is_hero_dead 是否因英雄全灭触发
|
||||
* @param _e 事件对象(未使用)
|
||||
* @param _is_hero_dead 是否因英雄全灭触发(当前未使用,保留签名以统一事件调用口径)
|
||||
* @param victory 是否胜利(默认 false)
|
||||
*/
|
||||
open_Victory(e: any, is_hero_dead: boolean = false, victory: boolean = false) {
|
||||
open_Victory(_e: any, _is_hero_dead: boolean = false, victory: boolean = false) {
|
||||
// 战斗结束(通关/失败),标记暂停状态以切断节间流转逻辑
|
||||
smc.mission.pause = true;
|
||||
this._victory = victory;
|
||||
@@ -765,23 +747,18 @@ export class MissionComp extends CCComp {
|
||||
// 失败:进度不变,结算窗走复活/再来一次/退出流程
|
||||
this._settleType = "defeat";
|
||||
} else {
|
||||
// 通关(Boss 节胜利):结算窗显示通关标题,确认退出并存档进下一关
|
||||
// 通关(Boss 回合胜利):无限关卡模式,存档永远推进到下一关第 1 节
|
||||
// (仅写存档,内存进度保留用于本局结算显示)
|
||||
this._settleType = "final";
|
||||
this._settleTitle = this.currentStage >= 10 ? "本章通关" : "本关通关";
|
||||
// 存档推进到下一关第 1 节(仅写存档,内存进度保留用于本局结算显示);
|
||||
// 第 10 关通关即本章全通,存档停留等待后续章节扩展
|
||||
if (this.currentStage < 10) {
|
||||
smc.updateStageProgress({
|
||||
chapter: this.currentChapter,
|
||||
stage: this.currentStage + 1,
|
||||
section: 1,
|
||||
});
|
||||
if ((smc as any).updateCloudData) {
|
||||
(smc as any).updateCloudData();
|
||||
}
|
||||
mLogger.log(this.debugMode, 'MissionComp',
|
||||
`[StageProgress] 本关通关,存档推进: ${this.currentChapter}-${this.currentStage + 1}-1`);
|
||||
this._settleTitle = "本关通关";
|
||||
smc.updateStageProgress({
|
||||
stage: this.currentStage + 1,
|
||||
});
|
||||
if ((smc as any).updateCloudData) {
|
||||
(smc as any).updateCloudData();
|
||||
}
|
||||
mLogger.log(this.debugMode, 'MissionComp',
|
||||
`[StageProgress] 本关通关,存档推进: ${this.currentStage + 1}-1`);
|
||||
}
|
||||
// 直接切入 BattleEnd,2s 后由 autoNextPhase 自动流转弹窗结算
|
||||
this.changePhase(MissionPhase.BattleEnd);
|
||||
@@ -836,20 +813,11 @@ export class MissionComp extends CCComp {
|
||||
this.currentWave = 1;
|
||||
this.isBossWave = false;
|
||||
this.skipRemainScoreOnBattleEnd = false;
|
||||
this.lastWaveClearTime = 0;
|
||||
this.updateRoundNodes();
|
||||
this.rewards = []
|
||||
this.revive_times = 1;
|
||||
this.lastTimeStr = "";
|
||||
this.lastTimeSecond = -1;
|
||||
this.memoryRefreshTimer = 0;
|
||||
this.lastMemoryText = "";
|
||||
this.perfDtAcc = 0;
|
||||
this.perfFrameCount = 0;
|
||||
this.heapBaseMB = -1;
|
||||
this.heapPeakMB = 0;
|
||||
this.heapTrendPerMinMB = 0;
|
||||
this.heapTrendTimer = 0;
|
||||
this.heapTrendBaseMB = -1;
|
||||
this.monsterCountSyncTimer = 0;
|
||||
|
||||
spawningEngine.reset();
|
||||
@@ -868,57 +836,48 @@ export class MissionComp extends CCComp {
|
||||
// ======================== 关卡进度管理 ========================
|
||||
|
||||
/**
|
||||
* 加载关卡进度存档(一关一局:只恢复大关,节恒从 1 开始)
|
||||
* 从 smc.data.stage_progress 恢复章节/大关,缺省从 1-1-1 开始
|
||||
* 加载关卡进度存档(一关一局:只恢复大关,回合恒从 1 开始)
|
||||
* 从 smc.data.stage_progress 恢复关卡号,缺省从第 1 关开始
|
||||
*/
|
||||
private loadStageProgress() {
|
||||
const progress = smc.data.stage_progress;
|
||||
if (progress && progress.chapter >= 1 && progress.stage >= 1) {
|
||||
this.currentChapter = progress.chapter;
|
||||
this.currentStage = progress.stage;
|
||||
} else {
|
||||
this.currentChapter = 1;
|
||||
this.currentStage = 1;
|
||||
}
|
||||
// 一关一局:进入关卡始终从第 1 节开打
|
||||
this.currentStage = Math.max(1, Math.floor(progress?.stage ?? 1));
|
||||
// 一关一局:进入关卡始终从第 1 回合开打
|
||||
this.currentSection = 1;
|
||||
// 加载完成后将权威进度镜像到 vmdata,驱动 HUD 实时显示
|
||||
smc.syncStageProgressToView();
|
||||
mLogger.log(this.debugMode, 'MissionComp',
|
||||
`[StageProgress] 加载进度: ${getProgressText(this.currentChapter, this.currentStage, this.currentSection)}`);
|
||||
`[StageProgress] 加载进度: ${getProgressText(this.currentStage, this.currentSection)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推进到下一小节(节间自动推进):
|
||||
* 1. 节号 +1,派发 SectionStart 触发 MissionMonComp 生成下一节怪物(从右侧跑入)。
|
||||
* 推进到下一回合(回合间自动推进):
|
||||
* 1. 回合号 +1,派发 SectionStart 触发 MissionMonComp 生成下一回合怪物(从右侧跑入)。
|
||||
* 2. 不清理英雄/技能实体、不重置局内数据、不重新三选一——英雄保留并自动归位(编队排队)。
|
||||
* 3. 进度实时镜像到 HUD,但不写持久存档(存档只在整关通关时推进到下一关)。
|
||||
*
|
||||
* 调用时机:PrepareStart 过场倒计时结束(autoNextPhase)。
|
||||
* 保证:仅 1~4 节清场后触发,末节(BOSS_SECTION)已走 open_Victory 结算,不会推进。
|
||||
* 保证:仅 1~4 回合清场后触发,末回合(BOSS_SECTION)已走 open_Victory 结算,不会推进。
|
||||
*/
|
||||
private advanceToNextSection() {
|
||||
this.currentSection++;
|
||||
// 节号实时同步到 HUD(内存进度,不持久化;整关通关时才统一写大关存档)。
|
||||
// 回合号实时同步到 HUD(内存进度,不持久化;整关通关时才统一写大关存档)。
|
||||
// 直接写 vmdata 视图层而非走 syncStageProgressToView(后者从 data 读,而 data 仍停留在大关起点)
|
||||
const view = smc.vmdata.stage_progress;
|
||||
view.chapter = this.currentChapter;
|
||||
view.stage = this.currentStage;
|
||||
view.section = this.currentSection;
|
||||
oops.message.dispatchEvent(GameEvent.STAGE_PROGRESS_UPDATE);
|
||||
// 触发 SectionStart 事件,MissionMonComp 据此生成下一节怪物(从右侧跑入)
|
||||
// 触发 SectionStart 事件,MissionMonComp 据此生成下一回合怪物(从右侧跑入)
|
||||
oops.message.dispatchEvent(GameEvent.SectionStart, {
|
||||
chapter: this.currentChapter,
|
||||
stage: this.currentStage,
|
||||
section: this.currentSection,
|
||||
});
|
||||
mLogger.log(this.debugMode, 'MissionComp',
|
||||
`[StageProgress] 进入小节: ${getProgressText(this.currentChapter, this.currentStage, this.currentSection)}`);
|
||||
`[StageProgress] 进入回合: ${getProgressText(this.currentStage, this.currentSection)}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重开本关:清理战场并重置数据,从本关第 1 节重新开始
|
||||
* Why: 一关一局,失败"再来一次"即整关重打(节恒从 1 开始)
|
||||
* 重开本关:清理战场并重置数据,从本关第 1 回合重新开始
|
||||
* Why: 一关一局,失败"再来一次"即整关重打(回合恒从 1 开始)
|
||||
*/
|
||||
private restartCurrentMission() {
|
||||
// 清理战场与局内数据
|
||||
@@ -929,21 +888,20 @@ export class MissionComp extends CCComp {
|
||||
this._victory = false;
|
||||
this._settleType = "defeat";
|
||||
this._settleTitle = "";
|
||||
// 一关一局:重开从第 1 节开始
|
||||
// 一关一局:重开从第 1 回合开始
|
||||
this.currentSection = 1;
|
||||
// 进入准备阶段
|
||||
this.changePhase(MissionPhase.Prepare);
|
||||
// 触发 MissionStart 重置开局免费抽卡
|
||||
oops.message.dispatchEvent(GameEvent.MissionStart, {});
|
||||
oops.message.dispatchEvent(GameEvent.FightReady);
|
||||
// 触发 SectionStart 重新生成本关第 1 节怪物
|
||||
// 触发 SectionStart 重新生成本关第 1 回合怪物
|
||||
oops.message.dispatchEvent(GameEvent.SectionStart, {
|
||||
chapter: this.currentChapter,
|
||||
stage: this.currentStage,
|
||||
section: this.currentSection,
|
||||
});
|
||||
mLogger.log(this.debugMode, 'MissionComp',
|
||||
`[StageProgress] 重开本关: ${getProgressText(this.currentChapter, this.currentStage, this.currentSection)}`);
|
||||
`[StageProgress] 重开本关: ${getProgressText(this.currentStage, this.currentSection)}`);
|
||||
}
|
||||
|
||||
// ======================== 回合管理 ========================
|
||||
@@ -959,7 +917,7 @@ export class MissionComp extends CCComp {
|
||||
* @param event 事件名
|
||||
* @param data { wave: number, bossWave?: boolean }
|
||||
*/
|
||||
private onNewWave(event: string, data: any) {
|
||||
private onNewWave(_event: string, data: any) {
|
||||
const wave = Number(data?.wave ?? 0);
|
||||
if (wave <= 0) return;
|
||||
|
||||
@@ -968,12 +926,46 @@ export class MissionComp extends CCComp {
|
||||
|
||||
this.currentWave = wave;
|
||||
smc.vmdata.mission_data.level = wave;
|
||||
this.updateRoundNodes();
|
||||
// 金币改为怪物死亡掉落(见 HeroAtkSystem),不再每回合固定发放
|
||||
this.lastTimeSecond = -1;
|
||||
this.clearTime = 0;
|
||||
this.update_time();
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新回合指示节点颜色:
|
||||
* - 已激活(已完成 + 当前回合)→ 黄色;当前回合且为 Boss 节 → 红色;未激活 → 白色。
|
||||
* Why: 回合推进时(onNewWave)与开局重置(data_init)调用,驱动 HUD 回合指示实时反馈。
|
||||
*/
|
||||
private updateRoundNodes() {
|
||||
const len = Math.min(this.round_nodes.length, SECTIONS_PER_STAGE);
|
||||
for (let i = 0; i < len; i++) {
|
||||
const node = this.round_nodes[i];
|
||||
if (!node || !node.isValid) continue;
|
||||
const round = i + 1;
|
||||
const color = round > this.currentWave
|
||||
? MissionComp.ROUND_INACTIVE
|
||||
: (round === this.currentWave && this.isBossWave)
|
||||
? MissionComp.ROUND_BOSS
|
||||
: MissionComp.ROUND_ACTIVE;
|
||||
// 优先染色 Sprite(图标配色),无 Sprite 时退化到 Label,均无则跳过
|
||||
const sprite = node.getComponent(Sprite);
|
||||
if (sprite) {
|
||||
sprite.color = color;
|
||||
} else {
|
||||
const label = node.getComponent(Label);
|
||||
if (label) label.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
// 进度条:第 1 回合 0%,每推进一回合 +25%(第 5 回合满格)
|
||||
if (this.round_progress && this.round_progress.isValid) {
|
||||
this.round_progress.progress =
|
||||
Math.max(0, Math.min(SECTIONS_PER_STAGE, this.currentWave) - 1) / (SECTIONS_PER_STAGE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 怪物数量管理 ========================
|
||||
|
||||
/**
|
||||
@@ -1182,115 +1174,6 @@ export class MissionComp extends CCComp {
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取战斗层的英雄和技能节点数量(用于性能监控) */
|
||||
private getBattleLayerNodeCount() {
|
||||
const scene = smc.map?.MapView?.scene;
|
||||
const layer = scene?.entityLayer?.node;
|
||||
if (!layer) return { heroNodes: 0, skillNodes: 0 };
|
||||
const heroRoot = layer.getChildByName("HERO");
|
||||
const skillRoot = layer.getChildByName("SKILL");
|
||||
return {
|
||||
heroNodes: heroRoot?.children.length || 0,
|
||||
skillNodes: skillRoot?.children.length || 0
|
||||
};
|
||||
}
|
||||
|
||||
// ======================== 性能监控面板 ========================
|
||||
|
||||
/** 性能监控相关代码 */
|
||||
|
||||
/** 初始化性能监控面板:在本组件节点下创建 Label */
|
||||
private initMemoryPanel() {
|
||||
if (!this.showMemoryPanel) return;
|
||||
let panel = this.node.getChildByName("mem_panel");
|
||||
if (!panel) {
|
||||
panel = new Node("mem_panel");
|
||||
panel.parent = this.node;
|
||||
panel.setPosition(0, -32, 0);
|
||||
}
|
||||
let label = panel.getComponent(Label);
|
||||
if (!label) {
|
||||
label = panel.addComponent(Label);
|
||||
}
|
||||
label.fontSize = 16;
|
||||
label.lineHeight = 20;
|
||||
this.memoryLabel = label;
|
||||
}
|
||||
|
||||
/** 移除性能监控面板 */
|
||||
private removeMemoryPanel() {
|
||||
const panel = this.node?.getChildByName("mem_panel");
|
||||
if (panel) {
|
||||
panel.destroy();
|
||||
}
|
||||
this.memoryLabel = null;
|
||||
this.lastMemoryText = "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新性能监控面板内容(每 0.5 秒一次):
|
||||
* 显示 堆内存 / 增长趋势 / 帧率 / 实体数量 / 对象池状态 等信息。
|
||||
*/
|
||||
private updateMemoryPanel(dt: number) {
|
||||
if (!this.showMemoryPanel || !this.memoryLabel) return;
|
||||
this.perfDtAcc += dt;
|
||||
this.perfFrameCount += 1;
|
||||
this.memoryRefreshTimer += dt;
|
||||
if (this.memoryRefreshTimer < 0.5) return;
|
||||
this.memoryRefreshTimer = 0;
|
||||
let heroCount = 0;
|
||||
ecs.query(this.heroViewMatcher).forEach(() => {
|
||||
heroCount++;
|
||||
});
|
||||
let skillCount = 0;
|
||||
ecs.query(this.skillViewMatcher).forEach(() => {
|
||||
skillCount++;
|
||||
});
|
||||
const monPool = Monster.getPoolStats();
|
||||
const skillPool = Skill.getPoolStats();
|
||||
const tooltipPool = Tooltip.getPoolStats();
|
||||
const layerNodes = this.getBattleLayerNodeCount();
|
||||
const perf = (globalThis as any).performance;
|
||||
const heapBytes = perf && perf.memory ? perf.memory.usedJSHeapSize : 0;
|
||||
let heapMB = heapBytes > 0 ? heapBytes / 1024 / 1024 : -1;
|
||||
if (heapMB > 0 && this.heapBaseMB < 0) {
|
||||
this.heapBaseMB = heapMB;
|
||||
this.heapPeakMB = heapMB;
|
||||
this.heapTrendBaseMB = heapMB;
|
||||
this.heapTrendTimer = 0;
|
||||
}
|
||||
if (heapMB > this.heapPeakMB) {
|
||||
this.heapPeakMB = heapMB;
|
||||
}
|
||||
this.heapTrendTimer += 0.5;
|
||||
if (heapMB > 0 && this.heapTrendBaseMB > 0 && this.heapTrendTimer >= 10) {
|
||||
const deltaMB = heapMB - this.heapTrendBaseMB;
|
||||
this.heapTrendPerMinMB = (deltaMB / this.heapTrendTimer) * 60;
|
||||
this.heapTrendBaseMB = heapMB;
|
||||
this.heapTrendTimer = 0;
|
||||
}
|
||||
const heapText = heapMB > 0 ? heapMB.toFixed(1) : "N/A";
|
||||
const heapDeltaText = this.heapBaseMB > 0 && heapMB > 0 ? (heapMB - this.heapBaseMB).toFixed(1) : "N/A";
|
||||
const heapPeakText = this.heapPeakMB > 0 ? this.heapPeakMB.toFixed(1) : "N/A";
|
||||
const avgDt = this.perfFrameCount > 0 ? this.perfDtAcc / this.perfFrameCount : 0;
|
||||
const fps = avgDt > 0 ? 1 / avgDt : 0;
|
||||
this.perfDtAcc = 0;
|
||||
this.perfFrameCount = 0;
|
||||
const text =
|
||||
`Heap:${heapText}MB Δ:${heapDeltaText} Peak:${heapPeakText}\n` +
|
||||
`Trend:${this.heapTrendPerMinMB.toFixed(2)}MB/min\n` +
|
||||
`Perf dt:${(avgDt * 1000).toFixed(1)}ms fps:${fps.toFixed(1)}\n` +
|
||||
`Ent H:${heroCount} S:${skillCount} N:${layerNodes.heroNodes}/${layerNodes.skillNodes}\n` +
|
||||
`Pool M:${monPool.total}(${monPool.paths}) K:${skillPool.total}(${skillPool.paths}) T:${tooltipPool.total}`;
|
||||
if (text === this.lastMemoryText) return;
|
||||
this.lastMemoryText = text;
|
||||
this.memoryLabel.string = text;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** ECS 组件移除时销毁节点 */
|
||||
reset() {
|
||||
this.PhaseTime = null as any;
|
||||
|
||||
@@ -124,12 +124,11 @@ export class MissionMonCompComp extends CCComp {
|
||||
start() { }
|
||||
|
||||
/**
|
||||
* 小节开始事件回调:生成怪物列表并按节奏刷出
|
||||
* 回合开始事件回调:生成怪物列表并按节奏刷出
|
||||
* @param event 事件名
|
||||
* @param data { chapter, stage, section }
|
||||
* @param data { stage, section }
|
||||
*/
|
||||
private onSectionStart(event: string, data: any) {
|
||||
const chapter = Number(data?.chapter ?? 1);
|
||||
const stage = Number(data?.stage ?? 1);
|
||||
const section = Number(data?.section ?? 1);
|
||||
|
||||
@@ -142,15 +141,15 @@ export class MissionMonCompComp extends CCComp {
|
||||
smc.vmdata.mission_data.mon_num = 0;
|
||||
smc.vmdata.mission_data.pending_mon_num = 0;
|
||||
|
||||
// 从生成引擎获取本小节怪物列表
|
||||
const monsters = spawningEngine.generateSectionMonsters(chapter, stage, section);
|
||||
// 从生成引擎获取本回合怪物列表
|
||||
const monsters = spawningEngine.generateSectionMonsters(stage, section);
|
||||
this.sectionTargetCount = monsters.length;
|
||||
|
||||
if (TestModeConfig.enable) {
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] 测试模式已开启");
|
||||
}
|
||||
mLogger.log(this.debugMode, 'MissionMonComp',
|
||||
`[MissionMonComp] 小节 ${chapter}-${stage}-${section} 生成完成,怪物总数: ${this.sectionTargetCount}`);
|
||||
`[MissionMonComp] 回合 ${stage}-${section} 生成完成,怪物总数: ${this.sectionTargetCount}`);
|
||||
|
||||
// 按批次拆分:batch=0 立即刷,batch=1 进入待刷队列
|
||||
const immediate: GeneratedMonster[] = [];
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
/**
|
||||
* @file RogueConfig.ts
|
||||
* @description 关卡刷怪系统(章节-关-节三级推进)
|
||||
* @description 无限关卡刷怪系统(关-回合两级推进)
|
||||
*
|
||||
* 游戏机制:
|
||||
* - 玩家线性推进 1 章 10 关 × 每关 5 小节(一关一局,节间自动推进)。
|
||||
* - 每关节点固定:1~3 节普通(3 小怪,立即全刷)、第 4 节精英(3 精英同刷)、
|
||||
* 第 5 节 Boss(2 小怪先刷,Boss 延迟 1s 刷出)。
|
||||
* - 怪物属性 = 基础 × 0.8 × (1 + 0.05×(lv-1)) × 类型倍率(精英1.5 / Boss3.0)。
|
||||
* - 玩家单线无限推进:从第 1 关起无上限,每关 5 回合(一关一局,回合间自动推进)。
|
||||
* - 第 1~4 回合普通怪(3 小怪立即全刷),属性逐回合轻量增强(SECTION_RAMP_STEP)。
|
||||
* - 第 5 回合单 Boss 登场(无护卫,延迟 1s 刷出),超厚血量(BOSS_HP_MUL)。
|
||||
* - Boss 加载时按攻击定位(近战/中程/远程)随机取型,并随机携带大招:
|
||||
* 前期 1 个,随关卡解锁扩展至多个(BOSS_ULT_UNLOCK_EVERY / BOSS_ULT_MAX_COUNT)。
|
||||
*
|
||||
* 设计层次:
|
||||
* 1. StageSet - 关卡配置(章节/关/节 + 数值公式)
|
||||
* 2. MonSkillPool/BossSkillPool - 怪物技能池(atking / atked / dead 等全触发类型)
|
||||
* 3. StageSpawningEngine - 生成引擎(按小节配置生成怪物列表)
|
||||
* 1. StageSet - 关卡数值公式(等级/成长/回合增强/Boss 血攻倍率)
|
||||
* 2. BossPools - Boss 池(按攻击定位分组,动态从 HeroInfo 提取)
|
||||
* 3. BossUltPools - Boss 大招池(按定位分组,引用 SkillSet 6100~6204 群体大招段)
|
||||
* 4. StageSpawningEngine - 生成引擎(按回合配置生成怪物列表)
|
||||
*
|
||||
* 核心公式:
|
||||
* 最终属性 = HeroInfo 基础属性 × MON_START_COEF × (1 + MON_GROW_SLOPE×(lv-1)) × 类型倍率
|
||||
* 1 级小怪 ≈ 英雄 × 0.8,10 级小怪 ≈ 英雄 × 1.16,保持平缓成长。
|
||||
* 小怪属性 = HeroInfo 基础属性 × MON_START_COEF × (1 + MON_GROW_SLOPE×(lv-1)) × 回合增强
|
||||
* Boss 属性 = 同上 × BOSS_HP_MUL(血)/ BOSS_AP_MUL(攻),等级 = 关卡号无限成长
|
||||
*/
|
||||
|
||||
import { HeroInfo, MonType, MonTypeName, TriggerGrouped } from "../common/config/heroSet";
|
||||
import { SkillOverrides } from "../common/config/SkillSet";
|
||||
import { HeroInfo, HType, MonType, TriggerGrouped, UltCdLv, UltCdSet } from "../common/config/heroSet";
|
||||
import { SkillOverrides, SkillSet } from "../common/config/SkillSet";
|
||||
import { FacSet } from "../common/config/GameSet";
|
||||
import {
|
||||
SectionType, SectionConfig, getSectionConfig, calcMonsterAttrs,
|
||||
NORMAL_MON_COUNT, NORMAL_FIRST_SPAWN, ELITE_COUNT, BOSS_ELITE_COUNT, BOSS_DELAY,
|
||||
MON_START_COEF, MON_GROW_SLOPE, ELITE_MUL, BOSS_MUL,
|
||||
NORMAL_MON_COUNT, NORMAL_FIRST_SPAWN, BOSS_DELAY,
|
||||
MON_START_COEF,
|
||||
} from "../common/config/StageSet";
|
||||
|
||||
// ======================== 1. 战斗常量 ========================
|
||||
@@ -79,126 +81,114 @@ export function getMonsterGoldDrop(monType: number, isBoss: boolean): number {
|
||||
return Math.max(0, Math.floor(isBoss ? cfg.boss : cfg.base));
|
||||
}
|
||||
|
||||
// ======================== 3. 怪物技能池 ========================
|
||||
// ======================== 3. Boss 池(按攻击定位分组) ========================
|
||||
|
||||
/**
|
||||
* 怪物技能套装(一套完整的触发技能配置)
|
||||
* 覆盖触发时机:call / atking / atked / dead / fstart / revive
|
||||
* Boss 池:按攻击定位(近战/中程/远程)分组,动态从 HeroInfo 提取。
|
||||
* 加载时随机一个定位 → 从对应池随机 Boss;定位池为空时自动回退(如暂无中程 Boss 配置)。
|
||||
*/
|
||||
export interface MonSkillSet {
|
||||
id: string;
|
||||
export const BossPools: Record<HType, number[]> = {
|
||||
[HType.Melee]: [],
|
||||
[HType.Mid]: [],
|
||||
[HType.Long]: [],
|
||||
};
|
||||
for (const key in HeroInfo) {
|
||||
const info = HeroInfo[key];
|
||||
if (info.fac !== FacSet.MON) continue;
|
||||
if (info.monType !== MonType.MeleeBoss && info.monType !== MonType.LongBoss) continue;
|
||||
const pool = BossPools[info.type as HType];
|
||||
if (pool) pool.push(info.uuid);
|
||||
}
|
||||
|
||||
// ======================== 4. Boss 大招池 ========================
|
||||
|
||||
/** 每解锁 1 个额外大招收紧的关卡间隔(每 5 关 +1 个大招位) */
|
||||
export const BOSS_ULT_UNLOCK_EVERY = 5;
|
||||
/** Boss 同时携带大招数量上限 */
|
||||
export const BOSS_ULT_MAX_COUNT = 3;
|
||||
|
||||
/**
|
||||
* 获取指定关卡的 Boss 携带大招数量(1 个起步,随关卡解锁扩展)
|
||||
* @param stage 关卡号(1~∞)
|
||||
* @returns 大招数量(1~BOSS_ULT_MAX_COUNT)
|
||||
*/
|
||||
export function getBossUltCount(stage: number): number {
|
||||
const st = Math.max(1, Math.floor(stage));
|
||||
return Math.min(BOSS_ULT_MAX_COUNT, 1 + Math.floor((st - 1) / BOSS_ULT_UNLOCK_EVERY));
|
||||
}
|
||||
|
||||
/** Boss 大招条目(s_uuid 引用 SkillSet 6100~6204 群体大招段,按定位分组) */
|
||||
export interface BossUltEntry {
|
||||
s_uuid: number;
|
||||
name: string;
|
||||
/** 普攻技能覆盖(可选,不填则使用怪物默认普攻) */
|
||||
skill?: { s_uuid: number; cd?: number; overrides?: SkillOverrides };
|
||||
/** 召唤触发 */
|
||||
call?: TriggerGrouped;
|
||||
/** 攻击触发 */
|
||||
atking?: TriggerGrouped;
|
||||
/** 受击触发 */
|
||||
atked?: TriggerGrouped;
|
||||
/** 死亡触发 */
|
||||
dead?: TriggerGrouped;
|
||||
/** 战斗开始触发 */
|
||||
fstart?: TriggerGrouped;
|
||||
/** 复活触发 */
|
||||
revive?: TriggerGrouped;
|
||||
/** 在技能池中的抽取权重 */
|
||||
/** 抽取权重 */
|
||||
weight: number;
|
||||
/** 技能参数覆盖(伤害系数等) */
|
||||
overrides?: SkillOverrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* 怪物技能池(硬编码)
|
||||
* 技能 uuid 引用 SkillSet 中的 6000~6500 段触发技能。
|
||||
* Boss 大招池(按攻击定位分组)
|
||||
* 大招注入 Boss 的 skills 表后由 SCastSystem 按 CD 自动施放(含镜头聚焦 + 技能名横幅)。
|
||||
*/
|
||||
export const MonSkillPool: Record<string, MonSkillSet> = {
|
||||
/** 狂暴:攻击触发自身攻击提升 */
|
||||
berserk: {
|
||||
id: "berserk", name: "狂暴", weight: 10,
|
||||
atking: {
|
||||
6401: [{ lv: 1, t_num: 5, overrides: { ap: 1 } }],
|
||||
},
|
||||
},
|
||||
/** 坚韧:受击获得护盾 */
|
||||
tough: {
|
||||
id: "tough", name: "坚韧", weight: 8,
|
||||
atked: {
|
||||
6301: [{ lv: 1, t_num: 3, overrides: { ap: 2 } }],
|
||||
},
|
||||
},
|
||||
/** 遗志:死亡时全队攻击提升 */
|
||||
legacy: {
|
||||
id: "legacy", name: "遗志", weight: 6,
|
||||
dead: {
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { ap: 3 } }],
|
||||
},
|
||||
},
|
||||
/** 战吼:战斗开始时全队攻击提升 */
|
||||
warcry: {
|
||||
id: "warcry", name: "战吼", weight: 5,
|
||||
fstart: {
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { ap: 2 } }],
|
||||
},
|
||||
},
|
||||
/** 吸血:攻击恢复生命 */
|
||||
leech: {
|
||||
id: "leech", name: "吸血", weight: 7,
|
||||
atking: {
|
||||
6302: [{ lv: 1, t_num: 4, overrides: { ap: 150 } }],
|
||||
},
|
||||
},
|
||||
export const BossUltPools: Record<HType, BossUltEntry[]> = {
|
||||
// 近战系:战士群体斩击(6100~6104 段)
|
||||
[HType.Melee]: [
|
||||
{ s_uuid: 6100, name: "寒冰斩", weight: 10, overrides: { ap: 180 } },
|
||||
{ s_uuid: 6101, name: "裂气斩", weight: 10, overrides: { ap: 180 } },
|
||||
{ s_uuid: 6102, name: "火焰斩", weight: 10, overrides: { ap: 180 } },
|
||||
{ s_uuid: 6103, name: "闪电斩", weight: 8, overrides: { ap: 180 } },
|
||||
{ s_uuid: 6104, name: "开天斩", weight: 6, overrides: { ap: 220 } },
|
||||
],
|
||||
// 中程系:射手箭雨 + 混合法术(6150~6151 / 6203~6204 段)
|
||||
[HType.Mid]: [
|
||||
{ s_uuid: 6150, name: "万箭齐发", weight: 10, overrides: { ap: 150 } },
|
||||
{ s_uuid: 6151, name: "火箭齐发", weight: 10, overrides: { ap: 150 } },
|
||||
{ s_uuid: 6203, name: "能量波", weight: 8, overrides: { ap: 150 } },
|
||||
{ s_uuid: 6204, name: "雷暴", weight: 8, overrides: { ap: 150 } },
|
||||
],
|
||||
// 远程系:法师群体法术(6200~6204 段)
|
||||
[HType.Long]: [
|
||||
{ s_uuid: 6200, name: "火球术", weight: 10, overrides: { ap: 160 } },
|
||||
{ s_uuid: 6201, name: "飓风", weight: 10, overrides: { ap: 140 } },
|
||||
{ s_uuid: 6202, name: "毒气弹", weight: 8, overrides: { ap: 150 } },
|
||||
{ s_uuid: 6203, name: "能量波", weight: 8, overrides: { ap: 150 } },
|
||||
{ s_uuid: 6204, name: "雷暴", weight: 8, overrides: { ap: 150 } },
|
||||
],
|
||||
};
|
||||
|
||||
/** Boss 专属技能池 */
|
||||
export const BossSkillPool: Record<string, MonSkillSet> = {
|
||||
/** 狂暴领主:攻击触发全队攻击提升 */
|
||||
boss_rage: {
|
||||
id: "boss_rage", name: "狂暴领主", weight: 10,
|
||||
atking: {
|
||||
6401: [{ lv: 1, t_num: 3, overrides: { ap: 3 } }],
|
||||
},
|
||||
dead: {
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { ap: 5 } }],
|
||||
},
|
||||
},
|
||||
/** 铁壁领主:受击获得高额护盾 */
|
||||
boss_iron: {
|
||||
id: "boss_iron", name: "铁壁领主", weight: 8,
|
||||
atked: {
|
||||
6301: [{ lv: 1, t_num: 2, overrides: { ap: 5 } }],
|
||||
},
|
||||
},
|
||||
/** 毁灭领主:战斗开始时全队攻击大幅提升 */
|
||||
boss_doom: {
|
||||
id: "boss_doom", name: "毁灭领主", weight: 6,
|
||||
fstart: {
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { ap: 8 } }],
|
||||
},
|
||||
atking: {
|
||||
6401: [{ lv: 1, t_num: 5, overrides: { ap: 2 } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ======================== 4. 配置校验 ========================
|
||||
// ======================== 5. 配置校验 ========================
|
||||
|
||||
/**
|
||||
* 校验 MonSkillPool / BossSkillPool 引用一致性
|
||||
* 校验 Boss 池 / Boss 大招池引用一致性
|
||||
* 建议在游戏启动时调用一次,便于发现配置错误
|
||||
* @returns 错误信息数组,空数组表示校验通过
|
||||
*/
|
||||
export function validateRogueConfig(): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
// 校验 Boss 类型在 MonList 中有怪
|
||||
if ((!MonList[MonType.MeleeBoss] || MonList[MonType.MeleeBoss].length === 0)
|
||||
&& (!MonList[MonType.LongBoss] || MonList[MonType.LongBoss].length === 0)) {
|
||||
errors.push("MonList 中无任何 Boss 类型(MeleeBoss/LongBoss)可用 uuid");
|
||||
// 校验至少一个定位有可用 Boss
|
||||
const hasBoss = (Object.keys(BossPools) as unknown as HType[]).some(t => BossPools[t].length > 0);
|
||||
if (!hasBoss) {
|
||||
errors.push("BossPools 中无任何可用 Boss uuid(MeleeBoss/LongBoss 均缺失)");
|
||||
}
|
||||
|
||||
// 校验大招池条目均存在于 SkillSet
|
||||
for (const key in BossUltPools) {
|
||||
const pool = BossPools[key as unknown as HType];
|
||||
const ults = BossUltPools[key as unknown as HType];
|
||||
if (!pool || pool.length === 0) continue; // 空定位池跳过(大招池无消费方)
|
||||
for (const ult of ults) {
|
||||
if (!SkillSet[ult.s_uuid]) {
|
||||
errors.push(`BossUltPools[${key}] 大招 ${ult.s_uuid}(${ult.name}) 未在 SkillSet 中定义`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
// ======================== 5. 生成结果接口 ========================
|
||||
// ======================== 6. 生成结果接口 ========================
|
||||
|
||||
/** 生成结果(传递给 MissionMonComp 用于实例化怪物) */
|
||||
export interface GeneratedMonster {
|
||||
@@ -207,10 +197,8 @@ export interface GeneratedMonster {
|
||||
hp: number;
|
||||
ap: number;
|
||||
isBoss: boolean;
|
||||
/** 是否为精英(精英关/Boss 关的精英) */
|
||||
/** 是否为精英(对象池复用实体需显式重置的标记位) */
|
||||
isElite?: boolean;
|
||||
/** 是否为 Boss 护卫队成员(供 UI/统计识别) */
|
||||
isBossGuard?: boolean;
|
||||
/** 怪物等级(驱动技能等级增量,Mon.load 第 6 参) */
|
||||
lv: number;
|
||||
spawnIndex: number;
|
||||
@@ -220,11 +208,15 @@ export interface GeneratedMonster {
|
||||
delay?: number;
|
||||
|
||||
/**
|
||||
* 怪物技能套装(覆盖全部触发时机)
|
||||
* 注入方式与 _testSkills 相同,在 Mon.load() 中写入 HeroAttrsComp
|
||||
* 怪物技能套装(技能注入通道,与 _testSkills 相同,在 Mon.load() 中写入 HeroAttrsComp)
|
||||
* - skill:替换普攻(会清空原技能表)
|
||||
* - ults:追加 Boss 大招(保留普攻,多个大招共存,SCastSystem 按 CD 轮转施放)
|
||||
* - 其余为触发技能组覆盖
|
||||
*/
|
||||
skills?: {
|
||||
skill?: { s_uuid: number; cd?: number; overrides?: SkillOverrides };
|
||||
/** Boss 大招列表(追加注入,不替换普攻) */
|
||||
ults?: { s_uuid: number; cd?: number; overrides?: SkillOverrides }[];
|
||||
call?: TriggerGrouped;
|
||||
atking?: TriggerGrouped;
|
||||
atked?: TriggerGrouped;
|
||||
@@ -234,7 +226,7 @@ export interface GeneratedMonster {
|
||||
};
|
||||
}
|
||||
|
||||
// ======================== 6. 测试模式配置 ========================
|
||||
// ======================== 7. 测试模式配置 ========================
|
||||
|
||||
export const TestModeConfig = {
|
||||
enable: false, // 默认关闭测试模式
|
||||
@@ -245,48 +237,43 @@ export const TestModeConfig = {
|
||||
spawnCount: 1,
|
||||
|
||||
skill: undefined as { s_uuid: number; cd?: number; overrides?: SkillOverrides } | undefined,
|
||||
/** 测试模式注入的大招列表(追加,不替换普攻) */
|
||||
ults: undefined as { s_uuid: number; cd?: number; overrides?: SkillOverrides }[] | undefined,
|
||||
atking: undefined as TriggerGrouped | undefined,
|
||||
atked: undefined as TriggerGrouped | undefined,
|
||||
dead: undefined as TriggerGrouped | undefined,
|
||||
fstart: undefined as TriggerGrouped | undefined,
|
||||
};
|
||||
|
||||
// ======================== 7. 生成引擎 ========================
|
||||
// ======================== 8. 生成引擎 ========================
|
||||
|
||||
/**
|
||||
* 关卡刷怪生成引擎
|
||||
* 按章节-关-节配置生成怪物列表,属性走 StageSet 数值公式
|
||||
* 无限关卡刷怪生成引擎
|
||||
* 按关-回合配置生成怪物列表,属性走 StageSet 数值公式
|
||||
*/
|
||||
export class StageSpawningEngine {
|
||||
|
||||
/**
|
||||
* 生成指定小节的怪物列表
|
||||
* @param chapter 章节号
|
||||
* @param stage 关卡号
|
||||
* @param section 小节号(1~10)
|
||||
* 生成指定回合的怪物列表
|
||||
* @param stage 关卡号(1~∞)
|
||||
* @param section 回合号(1~SECTIONS_PER_STAGE)
|
||||
* @returns 怪物列表(按刷出顺序排列)
|
||||
*/
|
||||
generateSectionMonsters(chapter: number, stage: number, section: number): GeneratedMonster[] {
|
||||
generateSectionMonsters(stage: number, section: number): GeneratedMonster[] {
|
||||
// 测试模式拦截:完全绕过生成引擎,直接返回硬编码测试怪
|
||||
if (TestModeConfig.enable) {
|
||||
return this.generateTestWave();
|
||||
}
|
||||
|
||||
const cfg = getSectionConfig(chapter, stage, section);
|
||||
const cfg = getSectionConfig(stage, section);
|
||||
if (!cfg) {
|
||||
// 配置缺失兜底:生成 3 只 Lv1 近战小怪
|
||||
return this.makeFallbackMonsters();
|
||||
}
|
||||
|
||||
switch (cfg.type) {
|
||||
case SectionType.Elite:
|
||||
return this.generateEliteSection(cfg);
|
||||
case SectionType.Boss:
|
||||
return this.generateBossSection(cfg);
|
||||
case SectionType.Normal:
|
||||
default:
|
||||
return this.generateNormalSection(cfg);
|
||||
}
|
||||
return cfg.type === SectionType.Boss
|
||||
? this.generateBossSection(cfg, stage)
|
||||
: this.generateNormalSection(cfg);
|
||||
}
|
||||
|
||||
/** 重置引擎状态(每局开始时调用,预留) */
|
||||
@@ -294,7 +281,8 @@ export class StageSpawningEngine {
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通节:NORMAL_MON_COUNT 只小怪全部立即刷出(batch=0)
|
||||
* 普通回合(1~4):NORMAL_MON_COUNT 只小怪全部立即刷出(batch=0),
|
||||
* 属性乘回合增强系数(cfg.ramp)实现逐回合轻量增强
|
||||
*/
|
||||
private generateNormalSection(cfg: SectionConfig): GeneratedMonster[] {
|
||||
const monsters: GeneratedMonster[] = [];
|
||||
@@ -304,64 +292,49 @@ export class StageSpawningEngine {
|
||||
const type = types[i % types.length];
|
||||
const mon = this.makeMonster(type, cfg.monLv, i);
|
||||
mon.batch = i < NORMAL_FIRST_SPAWN ? 0 : 1;
|
||||
// 回合内轻量增强:第 n 回合 × (1 + SECTION_RAMP_STEP×(n-1))
|
||||
mon.hp = Math.max(1, Math.round(mon.hp * cfg.ramp));
|
||||
mon.ap = Math.max(1, Math.round(mon.ap * cfg.ramp));
|
||||
monsters.push(mon);
|
||||
}
|
||||
return monsters;
|
||||
}
|
||||
|
||||
/**
|
||||
* 精英节:3 只精英同时刷出
|
||||
* Boss 回合(5):单 Boss 登场(无护卫)
|
||||
* Boss 延迟 BOSS_DELAY 秒刷出,属性由 makeBoss 按血/攻分离倍率计算(超厚血量)
|
||||
*/
|
||||
private generateEliteSection(cfg: SectionConfig): GeneratedMonster[] {
|
||||
const monsters: GeneratedMonster[] = [];
|
||||
const count = cfg.eliteCount ?? ELITE_COUNT;
|
||||
// 精英池:近战/重型/远程/刺客
|
||||
const eliteTypes = [MonType.Melee, MonType.Heavy, MonType.Long, MonType.Assassin];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const type = eliteTypes[i % eliteTypes.length];
|
||||
const mon = this.makeMonster(type, cfg.monLv, i);
|
||||
mon.isElite = true;
|
||||
mon.batch = 0;
|
||||
// 精英属性 = 普通 × ELITE_MUL
|
||||
mon.hp = Math.max(1, Math.round(mon.hp * ELITE_MUL));
|
||||
mon.ap = Math.max(1, Math.round(mon.ap * ELITE_MUL));
|
||||
monsters.push(mon);
|
||||
}
|
||||
return monsters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boss 节:2 小怪护卫 + 1 Boss
|
||||
* 小怪先刷(batch=0),Boss 延迟刷出(batch=0, delay=BOSS_DELAY)
|
||||
*/
|
||||
private generateBossSection(cfg: SectionConfig): GeneratedMonster[] {
|
||||
const monsters: GeneratedMonster[] = [];
|
||||
const eliteCount = cfg.eliteCount ?? BOSS_ELITE_COUNT;
|
||||
const eliteTypes = [MonType.Melee, MonType.Heavy];
|
||||
|
||||
// 先刷精英
|
||||
for (let i = 0; i < eliteCount; i++) {
|
||||
const type = eliteTypes[i % eliteTypes.length];
|
||||
const mon = this.makeMonster(type, cfg.monLv, i);
|
||||
mon.isElite = true;
|
||||
mon.isBossGuard = true;
|
||||
mon.batch = 0;
|
||||
mon.hp = Math.max(1, Math.round(mon.hp * ELITE_MUL));
|
||||
mon.ap = Math.max(1, Math.round(mon.ap * ELITE_MUL));
|
||||
monsters.push(mon);
|
||||
}
|
||||
|
||||
// Boss 延迟刷出
|
||||
const boss = this.makeBoss(cfg.monLv, monsters.length);
|
||||
private generateBossSection(cfg: SectionConfig, stage: number): GeneratedMonster[] {
|
||||
const boss = this.makeBoss(cfg.monLv, stage, 0);
|
||||
boss.batch = 0;
|
||||
boss.delay = BOSS_DELAY;
|
||||
// Boss 属性 = 普通 × BOSS_MUL
|
||||
boss.hp = Math.max(1, Math.round(boss.hp * BOSS_MUL));
|
||||
boss.ap = Math.max(1, Math.round(boss.ap * BOSS_MUL));
|
||||
monsters.push(boss);
|
||||
return [boss];
|
||||
}
|
||||
|
||||
return monsters;
|
||||
/** 生成普通怪物(属性按 StageSet 公式计算) */
|
||||
private makeMonster(type: MonType, monLv: number, spawnIndex: number): GeneratedMonster {
|
||||
let uuids = MonList[type];
|
||||
if (!uuids || uuids.length === 0) {
|
||||
// 兜底 Melee
|
||||
uuids = MonList[MonType.Melee] || [6001];
|
||||
}
|
||||
const uuid = uuids[Math.floor(Math.random() * uuids.length)];
|
||||
|
||||
const baseInfo = HeroInfo[uuid];
|
||||
const baseHp = baseInfo ? baseInfo.hp : 100;
|
||||
const baseAp = baseInfo ? baseInfo.ap : 10;
|
||||
const attrs = calcMonsterAttrs(baseHp, baseAp, monLv, false);
|
||||
|
||||
return {
|
||||
uuid,
|
||||
type,
|
||||
hp: attrs.hp,
|
||||
ap: attrs.ap,
|
||||
isBoss: false,
|
||||
lv: monLv,
|
||||
spawnIndex,
|
||||
batch: 0,
|
||||
};
|
||||
}
|
||||
|
||||
/** 配置缺失兜底:3 只 Lv1 近战小怪 */
|
||||
@@ -399,6 +372,7 @@ export class StageSpawningEngine {
|
||||
batch: 0,
|
||||
skills: {
|
||||
skill: TestModeConfig.skill,
|
||||
ults: TestModeConfig.ults,
|
||||
atking: TestModeConfig.atking,
|
||||
atked: TestModeConfig.atked,
|
||||
dead: TestModeConfig.dead,
|
||||
@@ -409,77 +383,74 @@ export class StageSpawningEngine {
|
||||
return monsters;
|
||||
}
|
||||
|
||||
/** 生成 Boss(基础面板按 StageSet 公式计算) */
|
||||
private makeBoss(monLv: number, spawnIndex: number): GeneratedMonster {
|
||||
const isMeleeBoss = Math.random() < 0.5;
|
||||
const type = isMeleeBoss ? MonType.MeleeBoss : MonType.LongBoss;
|
||||
/**
|
||||
* 生成 Boss:加载时随机攻击定位 → 从对应池随机 Boss 个体 → 随机携带大招
|
||||
* 属性走 calcMonsterAttrs(isBoss=true):HP × BOSS_HP_MUL(超厚血)/ AP × BOSS_AP_MUL
|
||||
*/
|
||||
private makeBoss(monLv: number, stage: number, spawnIndex: number): GeneratedMonster {
|
||||
const range = this.pickBossRange();
|
||||
const uuid = this.pickBossUuid(range);
|
||||
|
||||
// Boss 类型兜底:若 MonList 缺失则退回 MeleeBoss
|
||||
let uuids = MonList[type];
|
||||
if (!uuids || uuids.length === 0) {
|
||||
uuids = MonList[MonType.MeleeBoss] || MonList[MonType.Melee] || [6001];
|
||||
}
|
||||
const uuid = uuids[Math.floor(Math.random() * uuids.length)];
|
||||
|
||||
// Boss 基础面板取 HeroInfo 基础值,属性由调用方乘 BOSS_MUL
|
||||
const baseInfo = HeroInfo[uuid];
|
||||
const baseHp = baseInfo ? baseInfo.hp : 600;
|
||||
const baseAp = baseInfo ? baseInfo.ap : 30;
|
||||
const attrs = calcMonsterAttrs(baseHp, baseAp, monLv, false, false);
|
||||
const attrs = calcMonsterAttrs(baseHp, baseAp, monLv, true);
|
||||
|
||||
return {
|
||||
uuid,
|
||||
type,
|
||||
type: baseInfo?.monType ?? MonType.MeleeBoss,
|
||||
hp: attrs.hp,
|
||||
ap: attrs.ap,
|
||||
isBoss: true,
|
||||
lv: monLv,
|
||||
spawnIndex,
|
||||
batch: 0,
|
||||
skills: {
|
||||
ults: this.pickBossUlts(range, stage),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** 生成普通怪物(属性按 StageSet 公式计算) */
|
||||
private makeMonster(type: MonType, monLv: number, spawnIndex: number): GeneratedMonster {
|
||||
let uuids = MonList[type];
|
||||
/**
|
||||
* 随机 Boss 攻击定位(近战/中程/远程)
|
||||
* 空池自动回退:中程暂无 Boss 配置时落到远程/近战,后续补配即自动生效
|
||||
*/
|
||||
private pickBossRange(): HType {
|
||||
const all: HType[] = [HType.Melee, HType.Mid, HType.Long];
|
||||
const first = all[Math.floor(Math.random() * all.length)];
|
||||
if (BossPools[first].length > 0) return first;
|
||||
// 回退链:随机定位空池时按剩余定位找非空池
|
||||
const fallback = all.find(t => BossPools[t].length > 0);
|
||||
return fallback ?? HType.Melee;
|
||||
}
|
||||
|
||||
/** 从指定定位池随机取 Boss uuid(含全局兜底) */
|
||||
private pickBossUuid(range: HType): number {
|
||||
let uuids = BossPools[range];
|
||||
if (!uuids || uuids.length === 0) {
|
||||
// 兜底 Melee
|
||||
uuids = MonList[MonType.Melee] || [6001];
|
||||
uuids = MonList[MonType.MeleeBoss] || MonList[MonType.Melee] || [6001];
|
||||
}
|
||||
const uuid = uuids[Math.floor(Math.random() * uuids.length)];
|
||||
|
||||
const baseInfo = HeroInfo[uuid];
|
||||
const baseHp = baseInfo ? baseInfo.hp : 100;
|
||||
const baseAp = baseInfo ? baseInfo.ap : 10;
|
||||
const attrs = calcMonsterAttrs(baseHp, baseAp, monLv, false, false);
|
||||
|
||||
return {
|
||||
uuid,
|
||||
type,
|
||||
hp: attrs.hp,
|
||||
ap: attrs.ap,
|
||||
isBoss: false,
|
||||
lv: monLv,
|
||||
spawnIndex,
|
||||
batch: 0,
|
||||
};
|
||||
return uuids[Math.floor(Math.random() * uuids.length)];
|
||||
}
|
||||
|
||||
/** 从技能池中按权重抽取一套技能 */
|
||||
private pickSkillSet(pool: string[], library: Record<string, MonSkillSet>): GeneratedMonster["skills"] | undefined {
|
||||
const valid = pool.map(id => library[id]).filter(s => s);
|
||||
if (valid.length === 0) return undefined;
|
||||
const picked = this.pickWeighted(valid);
|
||||
if (!picked) return undefined;
|
||||
return {
|
||||
skill: picked.skill,
|
||||
call: picked.call,
|
||||
atking: picked.atking,
|
||||
atked: picked.atked,
|
||||
dead: picked.dead,
|
||||
fstart: picked.fstart,
|
||||
revive: picked.revive,
|
||||
};
|
||||
/**
|
||||
* 按定位抽取 Boss 大招(不重复),数量由关卡进度决定(getBossUltCount)
|
||||
* @returns 大招配置列表;池空时返回 undefined
|
||||
*/
|
||||
private pickBossUlts(range: HType, stage: number): GeneratedMonster["skills"]["ults"] {
|
||||
const pool = [...(BossUltPools[range] ?? [])];
|
||||
const count = Math.min(getBossUltCount(stage), pool.length);
|
||||
if (count <= 0) return undefined;
|
||||
|
||||
const ults: { s_uuid: number; cd?: number; overrides?: SkillOverrides }[] = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const picked = this.pickWeighted(pool);
|
||||
if (!picked) break;
|
||||
pool.splice(pool.indexOf(picked), 1);
|
||||
// 大招 CD 取英雄同档标准档(8s),与英雄大招节奏对齐
|
||||
ults.push({ s_uuid: picked.s_uuid, cd: UltCdSet[UltCdLv.Normal].cd, overrides: picked.overrides });
|
||||
}
|
||||
return ults.length > 0 ? ults : undefined;
|
||||
}
|
||||
|
||||
/** 通用加权随机抽取 */
|
||||
@@ -496,18 +467,6 @@ export class StageSpawningEngine {
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 8. 全局单例 ========================
|
||||
// ======================== 9. 全局单例 ========================
|
||||
|
||||
export const spawningEngine = new StageSpawningEngine();
|
||||
|
||||
// 保留旧接口兼容(逐步迁移期使用)
|
||||
/** @deprecated 请使用 StageSpawningEngine.generateSectionMonsters */
|
||||
export class RogueSpawningEngine extends StageSpawningEngine {
|
||||
/** @deprecated 请使用 generateSectionMonsters */
|
||||
generateBossCombo(): GeneratedMonster[] {
|
||||
return this.generateSectionMonsters(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated 请使用 spawningEngine */
|
||||
export const rogueSpawningEngine = new RogueSpawningEngine();
|
||||
|
||||
247
assets/script/game/map/TIBoxComp.ts
Normal file
247
assets/script/game/map/TIBoxComp.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* @file TIBoxComp.ts
|
||||
* @description 天赋详情弹窗组件(UI 视图层,TIBox.prefab 载体)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 展示天赋图标 / 名称 / 当前等级 / 条幅说明(getTalentDisplayText)。
|
||||
* 2. 展示层位需求行:前置层位名称(如:1 级天赋)、投入进度(如:5/5)、解锁勾图标(解锁亮 On,未解锁亮 Off)。
|
||||
* 3. 展示软前置行:前置天赋名称、等级进度(当前/需求,如:0/5)、前置已达成勾图标(达成亮 On,未达成亮 Off)。
|
||||
* 4. 加点按钮:仅在可加点(层位已解锁 + 前置满足 + 未满级 + 点数足够)时显示;
|
||||
* 不满足条件或已满级时隐藏,弹窗仍可正常打开查看。
|
||||
* 5. 加点成功后通过 TALENT_UPDATE 事件驱动 TalentsComp 全量刷新,并同步刷新本弹窗。
|
||||
*
|
||||
* 使用(oops.gui 模块):
|
||||
* oops.gui.open(UIID.TIBox, { uuid: 9001 });
|
||||
*
|
||||
* 依赖:
|
||||
* - TalentSet(TalentConfig 配置与校验:isTalentTierUnlocked/isTalentPrereqMet/getTalentUpgradeCost)
|
||||
* - smc —— 天赋等级表 / 点数 / upgradeTalent / 图标图集
|
||||
* - outlineRich —— RichText 条幅描边
|
||||
*/
|
||||
import { _decorator, EventTouch, Label, Node, NodeEventType, RichText, Sprite, SpriteFrame } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { TalentSet, TalentConfig, TalentTier, TALENT_TIER_UNLOCK_THRESHOLD, getTalentTierInvestedPoints, getTalentUpgradeCost, isTalentPrereqMet, isTalentTierUnlocked } from "../common/config/TalentSet";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { HRoleName } from "../common/config/heroSet";
|
||||
import { outlineRich } from "../common/RichOutline";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** TIBox 弹窗初始化参数 */
|
||||
export interface TIBoxParams {
|
||||
/** 天赋 uuid(TalentSet key) */
|
||||
uuid: number;
|
||||
}
|
||||
|
||||
/** TIBoxComp —— 天赋详情弹窗(任何状态均可点开,不可加点时隐藏加点按钮) */
|
||||
@ccclass('TIBoxComp')
|
||||
@ecs.register('TIBoxComp', false)
|
||||
export class TIBoxComp extends CCComp {
|
||||
|
||||
@property({ type: Sprite, tooltip: "天赋图标" })
|
||||
icon: Sprite = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "天赋名称" })
|
||||
lbl_name: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "当前天赋等级(当前/满级)" })
|
||||
lbl_level: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "前置天赋名称(无前置时隐藏前置行)" })
|
||||
lbl_prereq_name: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "前置天赋等级进度(当前/需求,如:0/5)" })
|
||||
lbl_prereq_lv: Label = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "前置已达成勾图标(On)" })
|
||||
node_prereq_check_on: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "前置未达成勾图标(Off)" })
|
||||
node_prereq_check_off: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "前置行容器(无前置天赋时整体隐藏)" })
|
||||
node_prereq: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "层位需求行容器(第 1 层天赋无层位需求,整体隐藏)" })
|
||||
node_tier: Node = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "前置层位名称(如:1 级天赋)" })
|
||||
lbl_tier_name: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "前一层投入进度(已投入/门槛,如:5/5)" })
|
||||
lbl_tier_progress: Label = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "层位已解锁勾图标(On)" })
|
||||
node_tier_check_on: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "层位未解锁勾图标(Off)" })
|
||||
node_tier_check_off: Node = null!;
|
||||
|
||||
@property({ type: RichText, tooltip: "条幅说明(当前已生效数值 / 每级引导)" })
|
||||
richtext_info: RichText = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "加点按钮(不可加点或满级时隐藏)" })
|
||||
node_add: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "关闭遮罩/关闭按钮(点击关闭弹窗)" })
|
||||
node_close: Node = null!;
|
||||
|
||||
/** 当前展示的天赋 uuid */
|
||||
private talent_uuid: number = 0;
|
||||
|
||||
protected onLoad(): void {
|
||||
this.node_add?.on(NodeEventType.TOUCH_END, this.onAddTap, this);
|
||||
this.node_close?.on(NodeEventType.TOUCH_END, this.onCloseTap, this);
|
||||
}
|
||||
|
||||
protected onDestroy(): void {
|
||||
this.node_add?.targetOff(this);
|
||||
this.node_close?.targetOff(this);
|
||||
oops.message.off(GameEvent.TALENT_UPDATE, this.refresh, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* oops.gui 添加到舞台回调(框架约定入口)
|
||||
* @param params 见 TIBoxParams
|
||||
*/
|
||||
onAdded(params: TIBoxParams): void {
|
||||
this.talent_uuid = params?.uuid ?? 0;
|
||||
// 加点成功后同步刷新本弹窗(等级/按钮状态),与 TalentsComp 同一事件源
|
||||
oops.message.on(GameEvent.TALENT_UPDATE, this.refresh, this);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/** 全量刷新弹窗内容 */
|
||||
private refresh(): void {
|
||||
const cfg = TalentSet[this.talent_uuid];
|
||||
if (!cfg) return;
|
||||
const levels = smc.getTalentLevels();
|
||||
const lv = levels[this.talent_uuid] ?? 0;
|
||||
|
||||
// 图标:技能图标图集(gui/ui7)优先,资源目录回退(与 TalentItemComp 口径一致)
|
||||
if (this.icon && cfg.icon) {
|
||||
let frame: SpriteFrame | null = null;
|
||||
if (smc.skillIconsAtlas) {
|
||||
frame = smc.skillIconsAtlas.getSpriteFrame(cfg.icon);
|
||||
}
|
||||
if (!frame) {
|
||||
frame = oops.res.get("game/heros/cards/" + cfg.icon, SpriteFrame) as SpriteFrame;
|
||||
}
|
||||
if (frame) this.icon.spriteFrame = frame;
|
||||
}
|
||||
|
||||
if (this.lbl_name) this.lbl_name.string = cfg.name;
|
||||
if (this.lbl_level) this.lbl_level.string = `${lv}/${cfg.max_lv}`;
|
||||
// 条幅说明:已加点显示当前生效数值,未加点显示每级引导
|
||||
if (this.richtext_info) this.richtext_info.string = outlineRich(this.buildDetailText(cfg, lv));
|
||||
|
||||
// 层位需求行:第 1 层恒解锁无需求,整体隐藏;其余层展示前一层名称与投入进度(如:1 级天赋 5/5)
|
||||
const hasTierNeed = cfg.tier > TalentTier.L1;
|
||||
if (this.node_tier) this.node_tier.active = hasTierNeed;
|
||||
if (hasTierNeed) {
|
||||
const prevTier = (cfg.tier - 1) as TalentTier;
|
||||
const invested = getTalentTierInvestedPoints(levels, prevTier);
|
||||
const tierMet = isTalentTierUnlocked(levels, cfg.tier);
|
||||
if (this.lbl_tier_name) this.lbl_tier_name.string = `${prevTier} 级天赋`;
|
||||
// 进度超门槛时截断显示(如已投入 7 点仍显示 5/5,避免 7/5 的歧义观感)
|
||||
const shown = Math.min(invested, TALENT_TIER_UNLOCK_THRESHOLD);
|
||||
if (this.lbl_tier_progress) this.lbl_tier_progress.string = `${shown}/${TALENT_TIER_UNLOCK_THRESHOLD}`;
|
||||
if (this.node_tier_check_on) this.node_tier_check_on.active = tierMet;
|
||||
if (this.node_tier_check_off) this.node_tier_check_off.active = !tierMet;
|
||||
}
|
||||
|
||||
// 软前置行:无前置整体隐藏;有前置展示名称/需求等级/达成勾
|
||||
const hasPrereq = !!(cfg.prereq_uuid && cfg.prereq_lv);
|
||||
if (this.node_prereq) this.node_prereq.active = hasPrereq;
|
||||
if (hasPrereq) {
|
||||
const preCfg = TalentSet[cfg.prereq_uuid!];
|
||||
const preMet = isTalentPrereqMet(levels, cfg);
|
||||
if (this.lbl_prereq_name) this.lbl_prereq_name.string = preCfg ? preCfg.name : `天赋${cfg.prereq_uuid}`;
|
||||
// 前置等级进度(当前/需求,如:0/5);超需求时截断显示,避免 7/5 的歧义观感
|
||||
const preLv = Math.min(levels?.[cfg.prereq_uuid!] ?? 0, cfg.prereq_lv!);
|
||||
if (this.lbl_prereq_lv) this.lbl_prereq_lv.string = `${preLv}/${cfg.prereq_lv}`;
|
||||
if (this.node_prereq_check_on) this.node_prereq_check_on.active = preMet;
|
||||
if (this.node_prereq_check_off) this.node_prereq_check_off.active = !preMet;
|
||||
}
|
||||
|
||||
// 加点按钮:层位解锁 + 前置满足 + 未满级 + 点数足够时才显示
|
||||
if (this.node_add) this.node_add.active = this.canUpgrade(lv, cfg.tier, levels);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按等级精准渲染天赋 info 模板({value}/{value2} 占位替换为实际数值)。
|
||||
* Lv0 时按 1 级数值预览(引导加点)。
|
||||
* @param cfg 天赋配置
|
||||
* @param lv 当前等级
|
||||
* @returns 替换占位后的完整描述文本
|
||||
*/
|
||||
private buildPreciseText(cfg: TalentConfig, lv: number): string {
|
||||
const mult = lv > 0 ? lv : 1;
|
||||
const v1 = cfg.bonuses[0]?.per_lv ?? cfg.t_num_reduce_per_lv ?? cfg.extra_trigger_chance_per_lv ?? 0;
|
||||
const v2 = cfg.bonuses[1]?.per_lv ?? 0;
|
||||
return cfg.info
|
||||
.replace("{value}", `${v1 * mult}`)
|
||||
.replace("{value2}", `${v2 * mult}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建天赋详情条幅文本:精准描述 + 下一级预览 + 满级提示
|
||||
* @param cfg 天赋配置
|
||||
* @param lv 当前等级
|
||||
*/
|
||||
private buildDetailText(cfg: TalentConfig, lv: number): string {
|
||||
const lines: string[] = [];
|
||||
// 精准效果描述:使用 cfg.info 模板按当前等级替换数值占位
|
||||
lines.push(this.buildPreciseText(cfg, lv));
|
||||
// 未满级:追加下一级预览
|
||||
if (lv < cfg.max_lv) {
|
||||
lines.push(`<color=#E0A93D>下一级:${this.buildPreciseText(cfg, lv + 1)}</color>`);
|
||||
} else {
|
||||
lines.push(`<color=#FFD700>已满级</color>`);
|
||||
}
|
||||
// 职业限定提示
|
||||
if (cfg.role !== undefined) {
|
||||
lines.push(`<color=#AAAAAA>仅${HRoleName[cfg.role]}生效</color>`);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前是否可加点(与 smc.upgradeTalent 校验口径一致,仅做 UI 预判定)
|
||||
* @param lv 当前等级
|
||||
* @param tier 所属层位
|
||||
* @param levels 天赋等级表
|
||||
*/
|
||||
private canUpgrade(lv: number, tier: number, levels: Record<number, number>): boolean {
|
||||
const cfg = TalentSet[this.talent_uuid];
|
||||
if (!cfg) return false;
|
||||
if (lv >= cfg.max_lv) return false;
|
||||
if (!isTalentTierUnlocked(levels, tier)) return false;
|
||||
if (!isTalentPrereqMet(levels, cfg)) return false;
|
||||
return smc.getTalentPoints() >= getTalentUpgradeCost(tier);
|
||||
}
|
||||
|
||||
/** 加点按钮点击:升级成功后由 TALENT_UPDATE 事件驱动刷新(含本弹窗) */
|
||||
private onAddTap(event?: EventTouch): void {
|
||||
if (event) event.propagationStopped = true;
|
||||
oops.audio.playEffect("music/button");
|
||||
smc.upgradeTalent(this.talent_uuid);
|
||||
}
|
||||
|
||||
/** 点击关闭 */
|
||||
private onCloseTap(event?: EventTouch): void {
|
||||
if (event) event.propagationStopped = true;
|
||||
oops.audio.playEffect("music/button");
|
||||
oops.gui.removeByNode(this.node);
|
||||
}
|
||||
|
||||
/** ECS 组件移除时销毁节点(CCComp 抽象方法实现) */
|
||||
reset(): void {
|
||||
if (this.node && this.node.isValid) {
|
||||
this.node.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/script/game/map/TIBoxComp.ts.meta
Normal file
9
assets/script/game/map/TIBoxComp.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "33249e41-30b1-42e3-a12e-f525b5580c37",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,40 +1,45 @@
|
||||
/**
|
||||
* @file TalentItemComp.ts
|
||||
* @description 天赋迷你升级卡组件(UI 视图层)
|
||||
* @description 天赋迷你升级卡组件(UI 视图层,编辑器静态摆放)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 接收 TalentsComp 下发的天赋配置 + 当前等级 + 阶位解锁态,渲染迷你点选卡。
|
||||
* 1. 通过编辑器配置的 uuid 自查询天赋配置 + 当前等级 + 层位解锁态,渲染迷你点选卡。
|
||||
* 2. 卡面自包含:图标 / 名称 / 当前已生效数值(Lv0 显示每级数值引导)/ 右下「当前/满级」角标。
|
||||
* 3. 阶位背景:普通背景节点下挂 blue / red / yellow 三个颜色子节点,按阶位激活其一(Ⅰ蓝 / Ⅱ红 / Ⅲ金)。
|
||||
* 4. 状态机:满级(MAX 金角标)/ 未激活(左上锁角标,不再做灰阶处理)。
|
||||
* 3. 层位背景:普通背景节点下挂 blue / red / yellow 三个颜色子节点,按层位映射激活其一(L1蓝 / L2红 / L3金,L4/L5 复用金)。
|
||||
* 4. 状态机:锁定(锁遮罩:层位未解锁或前置未满足;可加点时不显示,不做灰阶处理)/ 状态边框(满级 max 边框、已加点未满 active 边框,未加点均隐藏)。
|
||||
* 5. 点击回调上抛卡体点击,加点确认弹窗与升级校验统一由 TalentsComp 处理(单一决策点)。
|
||||
*
|
||||
* 依赖:
|
||||
* - TalentSet.TalentConfig / TalentTier / getTalentDisplayText —— 配置与卡面文案
|
||||
* - TalentSet(TalentConfig/TalentTier/getTalentDisplayText/isTalentTierUnlocked/isTalentPrereqMet)—— 配置与校验
|
||||
* - 背景三颜色子节点(blue / red / yellow,编辑器侧搭建)
|
||||
* - smc.uiconsAtlas —— 图标解析(缺省回退资源目录)
|
||||
* - smc —— 天赋等级表与图标图集(缺省回退资源目录)
|
||||
*/
|
||||
import { _decorator, Label, Node, NodeEventType, Sprite, SpriteFrame } from "cc";
|
||||
import { _decorator, CCInteger, Label, Node, NodeEventType, Sprite, SpriteFrame } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { TalentConfig, TalentTier, getTalentDisplayText } from "../common/config/TalentSet";
|
||||
import { TalentSet, TalentTier, getTalentDisplayText, isTalentPrereqMet, isTalentTierUnlocked } from "../common/config/TalentSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 阶位 → 背景节点下的颜色子节点名(Ⅰ蓝 / Ⅱ红 / Ⅲ金) */
|
||||
/** 层位 → 背景节点下的颜色子节点名(L1蓝 / L2红 / L3金;L4/L5 复用金) */
|
||||
const TIER_BG_CHILD_NAMES: Record<number, string> = {
|
||||
[TalentTier.Basic]: "blue",
|
||||
[TalentTier.Specialized]: "red",
|
||||
[TalentTier.Super]: "yellow",
|
||||
[TalentTier.L1]: "blue",
|
||||
[TalentTier.L2]: "red",
|
||||
[TalentTier.L3]: "yellow",
|
||||
[TalentTier.L4]: "yellow",
|
||||
[TalentTier.L5]: "yellow",
|
||||
};
|
||||
|
||||
/** TalentItemComp —— 天赋迷你升级卡组件 */
|
||||
/** TalentItemComp —— 天赋迷你升级卡组件(编辑器静态摆放,按 uuid 自驱动渲染) */
|
||||
@ccclass('TalentItemComp')
|
||||
@ecs.register('TalentItem', false)
|
||||
export class TalentItemComp extends CCComp {
|
||||
|
||||
@property({ type: CCInteger, tooltip: "天赋 uuid(TalentSet key,编辑器侧配置)" })
|
||||
talent_uuid: number = 0;
|
||||
|
||||
@property({ type: Label, tooltip: "天赋名称" })
|
||||
lbl_name: Label = null!;
|
||||
|
||||
@@ -47,20 +52,21 @@ export class TalentItemComp extends CCComp {
|
||||
@property({ type: Sprite, tooltip: "天赋图标" })
|
||||
icon: Sprite = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "卡牌背景节点(下挂 blue / red / yellow 三颜色子节点,按阶位激活其一)" })
|
||||
@property({ type: Node, tooltip: "卡牌背景节点(下挂 blue / red / yellow 三颜色子节点,按层位激活其一)" })
|
||||
bg: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "MAX 满级角标" })
|
||||
node_max: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "未激活锁遮罩/锁图标(未加点或阶位未解锁)" })
|
||||
@property({ type: Node, tooltip: "锁遮罩/锁图标(层位未解锁或前置未满足时显示;可加点时隐藏)" })
|
||||
node_lock: Node = null!;
|
||||
|
||||
/** 点击回调(卡体与 + 徽标同源上抛),由父级统一校验升级 */
|
||||
public onTalentTap: ((uuid: number) => void) | null = null;
|
||||
@property({ type: Node, tooltip: "满级边框(lv 达到 max_lv 时显示)" })
|
||||
node_border_max: Node = null!;
|
||||
|
||||
/** 当前展示的天赋 uuid(回调携带) */
|
||||
private curUuid: number = 0;
|
||||
@property({ type: Node, tooltip: "已加点边框(0 < lv < max_lv 时显示)" })
|
||||
node_border_active: Node = null!;
|
||||
|
||||
|
||||
/** 点击回调(卡体点击上抛),由 TalentsComp 统一校验升级 */
|
||||
public onTalentTap: ((uuid: number) => void) | null = null;
|
||||
|
||||
protected onLoad(): void {
|
||||
this.node.on(NodeEventType.TOUCH_END, this.onTap, this);
|
||||
@@ -70,25 +76,24 @@ export class TalentItemComp extends CCComp {
|
||||
this.node.off(NodeEventType.TOUCH_END, this.onTap, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新迷你卡展示
|
||||
* @param cfg 天赋配置(TalentSet 条目)
|
||||
* @param lv 当前等级
|
||||
* @param tierUnlocked 所属阶位是否已解锁
|
||||
*/
|
||||
public updateItem(cfg: TalentConfig, lv: number, tierUnlocked: boolean): void {
|
||||
/** 刷新迷你卡展示:按编辑器配置的 uuid 自查询配置与等级,层位/前置校验内部完成 */
|
||||
public updateItem(): void {
|
||||
const cfg = TalentSet[this.talent_uuid];
|
||||
if (!cfg) return;
|
||||
this.curUuid = cfg.uuid;
|
||||
const levels = smc.getTalentLevels();
|
||||
const lv = levels[this.talent_uuid] ?? 0;
|
||||
const tierUnlocked = isTalentTierUnlocked(levels, cfg.tier);
|
||||
const prereqMet = isTalentPrereqMet(levels, cfg);
|
||||
|
||||
if (this.lbl_name) this.lbl_name.string = cfg.name;
|
||||
// 卡面自包含:已加点显示当前生效数值,未加点显示每级数值引导
|
||||
if (this.lbl_info) this.lbl_info.string = getTalentDisplayText(cfg, lv);
|
||||
if (this.lbl_lv) this.lbl_lv.string = `${lv}/${cfg.max_lv}`;
|
||||
|
||||
// 未激活(Lv0 或阶位未解锁):仅显示 node_lock 锁遮罩,不做灰阶处理
|
||||
const inactive = lv <= 0 || !tierUnlocked;
|
||||
// 锁遮罩:仅在层位未解锁或前置未满足时显示;已解锁且前置满足即可加点,不再因 Lv0 挂锁
|
||||
const locked = !tierUnlocked || !prereqMet;
|
||||
|
||||
// 阶位背景:激活对应颜色子节点(Ⅰ蓝 / Ⅱ红 / Ⅲ金)
|
||||
// 层位背景:激活对应颜色子节点(L1蓝 / L2红 / L3金)
|
||||
if (this.bg) this.applyTierBg(cfg.tier);
|
||||
|
||||
// 图标:技能图标图集(gui/ui7)优先,资源目录回退(与 SCardComp 口径一致)
|
||||
@@ -103,12 +108,15 @@ export class TalentItemComp extends CCComp {
|
||||
if (frame) this.icon.spriteFrame = frame;
|
||||
}
|
||||
|
||||
// 状态角标:满级 MAX / 未激活锁角标(未加点或阶位未解锁,置于左上角)
|
||||
if (this.node_max) this.node_max.active = lv >= cfg.max_lv;
|
||||
if (this.node_lock) this.node_lock.active = inactive;
|
||||
// 锁遮罩:层位未解锁或前置未满足时显示
|
||||
if (this.node_lock) this.node_lock.active = locked;
|
||||
|
||||
// 状态边框:满级显示 max 边框;已加点未满显示 active 边框;未加点均隐藏
|
||||
if (this.node_border_max) this.node_border_max.active = lv >= cfg.max_lv;
|
||||
if (this.node_border_active) this.node_border_active.active = lv > 0 && lv < cfg.max_lv;
|
||||
}
|
||||
|
||||
/** 按阶位激活 bg 下对应颜色子节点,其余隐藏 */
|
||||
/** 按层位激活 bg 下对应颜色子节点,其余隐藏 */
|
||||
private applyTierBg(tier: TalentTier): void {
|
||||
const curName = TIER_BG_CHILD_NAMES[tier];
|
||||
for (const [, name] of Object.entries(TIER_BG_CHILD_NAMES)) {
|
||||
@@ -120,7 +128,7 @@ export class TalentItemComp extends CCComp {
|
||||
|
||||
/** 卡体点击上抛 uuid,加点确认与升级校验由 TalentsComp 处理 */
|
||||
private onTap(): void {
|
||||
this.onTalentTap?.(this.curUuid);
|
||||
this.onTalentTap?.(this.talent_uuid);
|
||||
}
|
||||
|
||||
/** ECS 组件移除时销毁节点(CCComp 抽象方法实现) */
|
||||
|
||||
@@ -1,65 +1,39 @@
|
||||
/**
|
||||
* @file TalentsComp.ts
|
||||
* @description 天赋加点页组件(UI 视图层)
|
||||
* @description 天赋加点页组件(UI 视图层,最小容器)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 三阶页签(Ⅰ 基础 / Ⅱ 专精 / Ⅲ 超强)状态机:单屏聚焦当前阶位的迷你天赋卡。
|
||||
* 2. 页签常显解锁进度(前阶投入 x/10),点击天赋卡弹出 CommonPrompt 确认加点(smc.upgradeTalent)。
|
||||
* 3. 监听 GameEvent.TALENT_UPDATE 全量重渲染(23 条卡片,无需虚拟列表)。
|
||||
* 1. 页头展示:可用天赋点 / 天赋等级(Lv.x)/ 当前等级经验进度 / 各层已投入点数。
|
||||
* 2. 收集子树内全部 TalentItemComp(编辑器静态摆放),TALENT_UPDATE 事件驱动全量刷新。
|
||||
* 3. 卡片点击统一打开 TIBox 天赋详情弹窗;加点校验(层位/前置/满级/点数)由 TIBoxComp 内聚,
|
||||
* 不满足条件时弹窗隐藏加点按钮,仅作详情查看。
|
||||
* 4. 看广告重置入口:CommonPrompt 二次确认后调 smc.resetTalents() 退还全部点数。
|
||||
*
|
||||
* 依赖:
|
||||
* - TalentSet(TalentSet/TalentTier/isTalentTierUnlocked/getTalentTierInvestedPoints)—— 配置与阶位校验
|
||||
* - TalentItemComp —— 单张迷你升级卡
|
||||
* - TalentSet(getTalentTierInvestedPoints/getTalentLevelExp)—— 配置与统计
|
||||
* - TalentItemComp —— 单张迷你升级卡(编辑器静态摆放,按 uuid 自驱动渲染)
|
||||
* - TIBoxComp —— 天赋详情弹窗(UIID.TIBox)
|
||||
* - smc —— 天赋等级表/点数/升级/重置
|
||||
*/
|
||||
import { _decorator, instantiate, Label, Node, NodeEventType, Prefab, ProgressBar, UITransform } from "cc";
|
||||
import { _decorator, Label, Node, NodeEventType, ProgressBar } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import {
|
||||
TalentSet, TalentTier,
|
||||
TALENT_TIER_UNLOCK_THRESHOLD,
|
||||
getTalentDisplayText, getTalentLevelExp, getTalentTierInvestedPoints, getTalentUpgradeCost, isTalentTierUnlocked,
|
||||
getTalentLevelExp, getTalentTierInvestedPoints,
|
||||
} from "../common/config/TalentSet";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { SmallTipComp } from "./SmallTipComp";
|
||||
import { TalentItemComp } from "./TalentItemComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 阶位页签展示文案(lockName 用于未解锁提示) */
|
||||
const TIER_TAB_TEXT: Record<number, { title: string; lockName: string }> = {
|
||||
[TalentTier.Basic]: { title: "基础天赋", lockName: "基础天赋" },
|
||||
[TalentTier.Specialized]: { title: "专精天赋", lockName: "基础天赋" },
|
||||
[TalentTier.Super]: { title: "传说天赋", lockName: "专精天赋" },
|
||||
};
|
||||
|
||||
/** 阶位页签选中高亮子节点名(可选,编辑器侧搭建) */
|
||||
const TAB_SELECTED_CHILD = "sp_on";
|
||||
|
||||
/** 阶位页签锁定图标子节点名(可选;阶位未解锁时显示) */
|
||||
const TAB_LOCKED_CHILD = "locked";
|
||||
|
||||
/** 阶位页签未选中态子节点名(可选;非当前选中页签时显示) */
|
||||
const TAB_INACTIVE_CHILD = "noactive";
|
||||
|
||||
/** 页签/卡片上的 smalltip 气泡节点名 */
|
||||
const SMALL_TIP_CHILD = "smalltip";
|
||||
|
||||
/** TalentsComp —— 天赋加点页组件(阶位 Tab 单屏聚焦) */
|
||||
/** TalentsComp —— 天赋加点页组件(最小容器:页头 + 编辑器静态卡片收集) */
|
||||
@ccclass('TalentsComp')
|
||||
@ecs.register('Talents', false)
|
||||
export class TalentsComp extends CCComp {
|
||||
|
||||
@property({ type: Node, tooltip: "当前阶位卡片列表容器(ScrollView content,Grid 三列)" })
|
||||
talents_content: Node = null!;
|
||||
|
||||
@property({ type: Prefab, tooltip: "迷你天赋卡预制(TalentItemComp)" })
|
||||
prefab_talent_item: Prefab = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "页头可用天赋点" })
|
||||
lbl_points: Label = null!;
|
||||
|
||||
@@ -69,52 +43,22 @@ export class TalentsComp extends CCComp {
|
||||
@property({ type: ProgressBar, tooltip: "当前等级经验进度条" })
|
||||
progress_exp: ProgressBar = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "一阶页签节点" })
|
||||
tab_basic: Node = null!;
|
||||
@property({ type: Label, tooltip: "当前等级经验数值(当前经验/升级所需经验)" })
|
||||
lbl_exp: Label = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "二阶页签节点" })
|
||||
tab_special: Node = null!;
|
||||
|
||||
@property({ type: Node, tooltip: "三阶页签节点" })
|
||||
tab_super: Node = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "一阶页签文案" })
|
||||
lbl_tab_basic: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "二阶页签文案" })
|
||||
lbl_tab_special: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "三阶页签文案" })
|
||||
lbl_tab_super: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "一阶已投入点数" })
|
||||
lbl_invest_basic: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "二阶已投入点数" })
|
||||
lbl_invest_special: Label = null!;
|
||||
|
||||
@property({ type: Label, tooltip: "三阶已投入点数" })
|
||||
lbl_invest_super: Label = null!;
|
||||
@property({ type: [Label], tooltip: "各层已投入点数 Label(数组索引对应第 1~5 层)" })
|
||||
lbl_tier_points: Label[] = [];
|
||||
|
||||
@property({ type: Node, tooltip: "看广告重置按钮" })
|
||||
btn_reset: Node = null!;
|
||||
|
||||
/** 当前选中的阶位(首次进入默认为已解锁的最高阶,之后由玩家切换) */
|
||||
private curTier: TalentTier = TalentTier.Basic;
|
||||
|
||||
/** 是否已完成首次默认阶位选择 */
|
||||
private inited: boolean = false;
|
||||
|
||||
/** 阶位 → 页签节点/文案/投入点数/气泡 映射(onLoad 时装配) */
|
||||
private tabNodes: Map<TalentTier, { node: Node | null; label: Label | null; invest: Label | null; tip: SmallTipComp | null }> = new Map();
|
||||
/** 页内全部天赋卡(onLoad 时自动收集子树内所有 TalentItemComp,无需编辑器拖引) */
|
||||
private talent_items: TalentItemComp[] = [];
|
||||
|
||||
protected onLoad(): void {
|
||||
this.tabNodes.set(TalentTier.Basic, { node: this.tab_basic, label: this.lbl_tab_basic, invest: this.lbl_invest_basic, tip: this.getSmallTip(this.tab_basic) });
|
||||
this.tabNodes.set(TalentTier.Specialized, { node: this.tab_special, label: this.lbl_tab_special, invest: this.lbl_invest_special, tip: this.getSmallTip(this.tab_special) });
|
||||
this.tabNodes.set(TalentTier.Super, { node: this.tab_super, label: this.lbl_tab_super, invest: this.lbl_invest_super, tip: this.getSmallTip(this.tab_super) });
|
||||
|
||||
for (const [tier, tab] of this.tabNodes) {
|
||||
tab.node?.on(NodeEventType.TOUCH_END, () => this.selectTier(tier), this);
|
||||
this.talent_items = this.node.getComponentsInChildren(TalentItemComp);
|
||||
for (const item of this.talent_items) {
|
||||
item.onTalentTap = (uuid: number) => this.onTalentTap(uuid);
|
||||
}
|
||||
this.btn_reset?.on(NodeEventType.TOUCH_END, this.onResetTap, this);
|
||||
}
|
||||
@@ -130,25 +74,30 @@ export class TalentsComp extends CCComp {
|
||||
}
|
||||
|
||||
protected onDestroy(): void {
|
||||
for (const [, tab] of this.tabNodes) {
|
||||
tab.node?.targetOff(this);
|
||||
}
|
||||
this.btn_reset?.targetOff(this);
|
||||
}
|
||||
|
||||
/** 切换阶位页签:锁定阶允许查看(卡片呈锁定态),不弹解锁提示 */
|
||||
private selectTier(tier: TalentTier): void {
|
||||
if (tier === this.curTier) return;
|
||||
this.curTier = tier;
|
||||
this.refreshUI();
|
||||
}
|
||||
|
||||
/** 全量刷新:页头点数 + 页签解锁进度 + 当前阶位卡片列表 */
|
||||
/** 全量刷新:页头点数 + 天赋等级/经验 + 全部静态卡片 */
|
||||
public refreshUI(): void {
|
||||
if (!this.talents_content || !this.prefab_talent_item) return;
|
||||
const levels = smc.getTalentLevels();
|
||||
|
||||
if (this.lbl_points) this.lbl_points.string = `${smc.getTalentPoints()}`;
|
||||
if (this.lbl_points) {
|
||||
// 剩余/总点数:总点数 = 剩余 + 各层已投入
|
||||
const remaining = smc.getTalentPoints();
|
||||
let invested = 0;
|
||||
for (let tier = 1; tier <= 5; tier++) {
|
||||
invested += getTalentTierInvestedPoints(levels, tier);
|
||||
}
|
||||
this.lbl_points.string = `${remaining}/${remaining + invested}`;
|
||||
}
|
||||
|
||||
// 各层已投入点数(数组索引对应层 1~5)
|
||||
for (let tier = 1; tier <= this.lbl_tier_points.length; tier++) {
|
||||
const lbl = this.lbl_tier_points[tier - 1];
|
||||
if (lbl) {
|
||||
lbl.string = `${getTalentTierInvestedPoints(levels, tier)}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 天赋等级(Lv.x)与当前等级经验进度
|
||||
const talentLv = smc.getTalentLevel();
|
||||
@@ -160,127 +109,25 @@ export class TalentsComp extends CCComp {
|
||||
? Math.min(1, smc.getTalentExp() / needExp)
|
||||
: 1;
|
||||
}
|
||||
|
||||
// 仅首次进入时默认选中已解锁的最高阶,后续刷新保持玩家当前选择
|
||||
if (!this.inited) {
|
||||
this.inited = true;
|
||||
if (isTalentTierUnlocked(levels, TalentTier.Super)) this.curTier = TalentTier.Super;
|
||||
else if (isTalentTierUnlocked(levels, TalentTier.Specialized)) this.curTier = TalentTier.Specialized;
|
||||
if (this.lbl_exp) {
|
||||
const needExp = getTalentLevelExp(talentLv);
|
||||
// 满级后显示为 0/0,避免分母为 0
|
||||
this.lbl_exp.string = needExp > 0
|
||||
? `${smc.getTalentExp()}/${needExp}`
|
||||
: `0/0`;
|
||||
}
|
||||
|
||||
this.refreshTabs(levels);
|
||||
this.refreshGrid(levels);
|
||||
}
|
||||
|
||||
/** 页签文案 + 各阶已投入点数 + 选中高亮(sp_on)+ 锁定图标(locked)+ 未选中态(noactive) */
|
||||
private refreshTabs(levels: Record<number, number>): void {
|
||||
for (const [tier, tab] of this.tabNodes) {
|
||||
const text = TIER_TAB_TEXT[tier];
|
||||
const unlocked = isTalentTierUnlocked(levels, tier);
|
||||
if (tab.label) tab.label.string = text.title;
|
||||
// 各阶已投入点数(该阶所有天赋等级之和)
|
||||
if (tab.invest) tab.invest.string = `${getTalentTierInvestedPoints(levels, tier)}`;
|
||||
// 锁定图标:该阶未解锁时显示,解锁后隐藏(一阶恒解锁,节点可缺省)
|
||||
const locked = tab.node?.getChildByName(TAB_LOCKED_CHILD);
|
||||
if (locked) locked.active = !unlocked;
|
||||
|
||||
const selected = tier === this.curTier;
|
||||
// 未选中态节点:非当前激活页签时显示(与 sp_on 选中高亮互斥)
|
||||
const inactive = tab.node?.getChildByName(TAB_INACTIVE_CHILD);
|
||||
if (inactive) inactive.active = !selected;
|
||||
|
||||
tab.node?.getChildByName(TAB_SELECTED_CHILD)?.setPosition(0, 0);
|
||||
tab.node?.getChildByName(TAB_SELECTED_CHILD) && (tab.node.getChildByName(TAB_SELECTED_CHILD)!.active = selected);
|
||||
}
|
||||
}
|
||||
|
||||
/** 重建当前阶位的卡片列表(每阶 ≤10 条,直接全量实例化) */
|
||||
private refreshGrid(levels: Record<number, number>): void {
|
||||
this.talents_content.removeAllChildren();
|
||||
const unlocked = isTalentTierUnlocked(levels, this.curTier);
|
||||
const cfgs = Object.values(TalentSet)
|
||||
.filter(cfg => cfg.tier === this.curTier)
|
||||
.sort((a, b) => a.uuid - b.uuid);
|
||||
|
||||
let cardHeight = 0;
|
||||
for (const cfg of cfgs) {
|
||||
const lv = levels[cfg.uuid] ?? 0;
|
||||
|
||||
const itemNode = instantiate(this.prefab_talent_item);
|
||||
this.talents_content.addChild(itemNode);
|
||||
const comp = itemNode.getComponent(TalentItemComp);
|
||||
if (!comp) continue;
|
||||
comp.updateItem(cfg, lv, unlocked);
|
||||
comp.onTalentTap = (uuid: number) => this.onTalentTap(uuid);
|
||||
if (!cardHeight) cardHeight = itemNode.getComponent(UITransform)?.height ?? 0;
|
||||
}
|
||||
|
||||
// Grid 三列手算容器高度(与旧版图鉴页口径一致,spacing 15 / 上下 padding 15)
|
||||
const rows = Math.ceil(cfgs.length / 3);
|
||||
if (cardHeight > 0 && rows > 0) {
|
||||
const contentHeight = rows * cardHeight + (rows - 1) * 15 + 30;
|
||||
const uiTransform = this.talents_content.getComponent(UITransform);
|
||||
if (uiTransform) uiTransform.height = contentHeight;
|
||||
for (const item of this.talent_items) {
|
||||
item.updateItem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取挂载点下的 smalltip 气泡组件(节点缺失或组件未挂时返回 null 并静默跳过提示)
|
||||
* @param parent 气泡挂载的父节点
|
||||
* 卡片点击统一入口:任何状态均打开 TIBox 天赋详情弹窗,加点按钮显隐由 TIBoxComp 校验
|
||||
* @param uuid 天赋 uuid
|
||||
*/
|
||||
private getSmallTip(parent: Node | null): SmallTipComp | null {
|
||||
const tipNode = parent?.getChildByName(SMALL_TIP_CHILD);
|
||||
return tipNode ? (tipNode.getComponent(SmallTipComp) ?? tipNode.addComponent(SmallTipComp)) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定节点下弹出 smalltip 气泡提示
|
||||
* @param anchor 气泡挂载的父节点(优先页签,卡片点击时传卡片节点)
|
||||
* @param text 提示文案
|
||||
*/
|
||||
private showSmallTip(anchor: Node | null, text: string): void {
|
||||
const tip = this.getSmallTip(anchor);
|
||||
tip?.show(text);
|
||||
}
|
||||
|
||||
/** 卡片点击统一入口:未解锁/已满级/点数不足走 smalltip 气泡;其余弹出 CommonPrompt 确认加点,成功后事件驱动刷新 */
|
||||
private onTalentTap(uuid: number): void {
|
||||
const cfg = TalentSet[uuid];
|
||||
if (!cfg) return;
|
||||
const levels = smc.getTalentLevels();
|
||||
|
||||
const tab = this.tabNodes.get(cfg.tier);
|
||||
if (!isTalentTierUnlocked(levels, cfg.tier)) {
|
||||
const invested = getTalentTierInvestedPoints(levels, (cfg.tier - 1) as TalentTier);
|
||||
this.showSmallTip(tab?.node ?? null, `${TIER_TAB_TEXT[cfg.tier].lockName}投入${TALENT_TIER_UNLOCK_THRESHOLD} 点解锁`);
|
||||
return;
|
||||
}
|
||||
const lv = smc.getTalentLv(uuid);
|
||||
if (lv >= cfg.max_lv) {
|
||||
this.showSmallTip(tab?.node ?? null, "该天赋已满级");
|
||||
return;
|
||||
}
|
||||
const cost = getTalentUpgradeCost(cfg.tier);
|
||||
if (cost <= 0) return;
|
||||
if (smc.getTalentPoints() < cost) {
|
||||
this.showSmallTip(tab?.node ?? null, "天赋点不足");
|
||||
return;
|
||||
}
|
||||
|
||||
// CommonPrompt 确认加点(UIID.Window)
|
||||
oops.gui.open(UIID.Window, {
|
||||
title: "天赋加点",
|
||||
content: `${cfg.name} Lv.${lv} → Lv.${lv + 1}\n消耗 <color=#E0A93D>${cost}</color> 点天赋点,获得 ${getTalentDisplayText(cfg, 1)}。是否加点?`,
|
||||
okWord: "加点",
|
||||
cancelWord: "取消",
|
||||
needCancel: true,
|
||||
maskClose: true,
|
||||
okFunc: () => {
|
||||
if (!smc.upgradeTalent(uuid)) {
|
||||
this.showSmallTip(this.tabNodes.get(cfg.tier)?.node ?? null, "天赋点不足");
|
||||
}
|
||||
},
|
||||
});
|
||||
oops.gui.open(UIID.TIBox, { uuid });
|
||||
}
|
||||
|
||||
/** 看广告重置入口:CommonPrompt 二次确认(广告接入前先直接确认重置) */
|
||||
|
||||
@@ -2,7 +2,7 @@ import { _decorator, Animation, CCFloat, CCInteger, Collider2D, Contact2DType, E
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { HeroViewComp } from "../hero/HeroViewComp";
|
||||
import { DTType, EType, RType, SkillConfig, SkillSet } from "../common/config/SkillSet";
|
||||
import { DTType, EType, RType, SkillConfig, SkillSet, SummonTMode } from "../common/config/SkillSet";
|
||||
import { SDataCom } from "./SDataCom";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { DamageQueueHelper } from "../hero/DamageQueueComp";
|
||||
@@ -54,6 +54,22 @@ export class SkillView extends CCComp {
|
||||
@property({ type: CCInteger, slide: true, range: [1, 8, 1], tooltip: "单次施法召唤的技能实体个数" })
|
||||
summon_count: number = 1;
|
||||
|
||||
/** 召唤目标选择模式:same=锁定同一敌人(默认);random=每次出手随机敌人;unique=避免重复敌人(一轮选完重置) */
|
||||
@property({ type: Enum(SummonTMode), tooltip: "召唤目标选择:同一敌人/随机敌人/避免相同敌人" })
|
||||
summon_target_mode: SummonTMode = SummonTMode.same;
|
||||
|
||||
/** 召唤物 X 轴步进偏移(像素):summon_count>1 时以出生点/目标点为中心对称交替铺开(第1个=0,之后按 +1,-1,+2,-2... 档 × 该值,正=右) */
|
||||
@property({ type: CCFloat, slide: true, range: [-200, 200, 5], tooltip: "召唤物X轴步进偏移:以中心对称交替铺开(0,+1,-1,+2,-2...档×该值)" })
|
||||
summon_x_offset: number = 0;
|
||||
|
||||
/** 召唤物 Y 轴步进偏移(像素):summon_count>1 时与 X 同档位对称交替铺开(正=上) */
|
||||
@property({ type: CCFloat, slide: true, range: [-200, 200, 5], tooltip: "召唤物Y轴步进偏移:与X同档位对称交替(0,+1,-1,+2,-2...档×该值)" })
|
||||
summon_y_offset: number = 0;
|
||||
|
||||
/** 召唤间隔(秒):summon_count>1 时相邻两个实体的生成时间间隔(<=0 回退全局 FightSet.SUMMON_INTERVAL) */
|
||||
@property({ type: CCFloat, slide: true, range: [0, 2, 0.05], tooltip: "召唤间隔(秒):相邻实体生成间隔,0=用全局默认" })
|
||||
summon_interval: number = 0;
|
||||
|
||||
/** 抛射物高度增量(像素),贝塞尔弹道时叠加到 bezierMidHeight,控制抛物线顶点抬高 */
|
||||
@property({ type: CCFloat, slide: true, range: [0, 500, 5], tooltip: "抛射物高度增量(叠加到贝塞尔弹道中点高度)" })
|
||||
height_offset: number = 0;
|
||||
|
||||
233
docs/软著/gen_docx.mjs
Normal file
233
docs/软著/gen_docx.mjs
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* 软著材料 docx 生成脚本
|
||||
* 1) 源程序:解析已生成的 txt,按显示宽度折行(每行 ≤84 列),每页 60 行,
|
||||
* 页眉左侧"软件全称 版本号"、右侧 Word 自动页码域"第 X 页 共 Y 页"。
|
||||
* 2) 设计说明书:解析 md,生成封面 + 章节正文 docx。
|
||||
* 用法:node gen_docx.mjs
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {
|
||||
Document, Packer, Paragraph, TextRun, Header, AlignmentType,
|
||||
PageNumber, TabStopType, HeadingLevel,
|
||||
} from 'docx';
|
||||
|
||||
const DIR = import.meta.dirname;
|
||||
const SRC_TXT = path.join(DIR, '像素大勇者游戏软件_V1.0_源程序.txt');
|
||||
const MANUAL_MD = path.join(DIR, '像素大勇者游戏软件_V1.0_设计说明书.md');
|
||||
const OUT_SRC = path.join(DIR, '像素大勇者游戏软件_V1.0_源程序.docx');
|
||||
const OUT_MANUAL = path.join(DIR, '像素大勇者游戏软件_V1.0_设计说明书.docx');
|
||||
|
||||
const SOFTWARE = '像素大勇者游戏软件 V1.0';
|
||||
const LINES_PER_PAGE = 60;
|
||||
const FOLD_WIDTH = 84; // A4 折行限制:显示列宽(ASCII=1 列,中文=2 列)
|
||||
|
||||
// A4 纵向,边距:上下 1.27cm,左右 1.5cm
|
||||
const PAGE = {
|
||||
size: { width: 11906, height: 16838 },
|
||||
margin: { top: 720, bottom: 720, left: 850, right: 850, header: 400 },
|
||||
};
|
||||
const TEXT_WIDTH = PAGE.size.width - PAGE.margin.left - PAGE.margin.right;
|
||||
|
||||
/** 统一页眉:左侧软件名+版本号,右侧自动页码域 */
|
||||
function makeHeader() {
|
||||
return new Header({
|
||||
children: [
|
||||
new Paragraph({
|
||||
tabStops: [{ type: TabStopType.RIGHT, position: TEXT_WIDTH }],
|
||||
children: [
|
||||
new TextRun({ text: SOFTWARE, size: 18, font: { ascii: 'Consolas', eastAsia: 'SimSun' } }),
|
||||
new TextRun({
|
||||
children: ['\t第 ', PageNumber.CURRENT, ' 页 共 ', PageNumber.TOTAL_PAGES, ' 页'],
|
||||
size: 18, font: { ascii: 'Consolas', eastAsia: 'SimSun' },
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 一、源程序 docx
|
||||
// ============================================================
|
||||
|
||||
/** 字符显示宽度(ASCII 记 1 列,全角/中文记 2 列) */
|
||||
const charWidth = (ch) => (ch.charCodeAt(0) > 0xff ? 2 : 1);
|
||||
|
||||
/** 超宽行按显示列宽折行,续行缩进 4 空格 */
|
||||
function foldLine(line, width = FOLD_WIDTH) {
|
||||
if ([...line].reduce((w, c) => w + charWidth(c), 0) <= width) return [line];
|
||||
const out = [];
|
||||
let cur = '', curW = 0;
|
||||
for (const ch of line) {
|
||||
const cw = charWidth(ch);
|
||||
if (curW + cw > width) {
|
||||
out.push(cur);
|
||||
cur = ' ' + ch; // 续行缩进
|
||||
curW = 4 + cw;
|
||||
} else {
|
||||
cur += ch;
|
||||
curW += cw;
|
||||
}
|
||||
}
|
||||
if (cur) out.push(cur);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 解析 txt:剔除页眉/分隔线/页间空行,还原连续代码行 */
|
||||
function parseSourceTxt() {
|
||||
const raw = fs.readFileSync(SRC_TXT, 'utf8').split('\n');
|
||||
const codeLines = [];
|
||||
let count = 0;
|
||||
for (const line of raw) {
|
||||
if (line.startsWith('像素大勇者游戏软件')) continue; // 页眉行
|
||||
if (/^=+$/.test(line.trim()) && line.trim() !== '') continue; // 分隔线
|
||||
if (count > 0 && count % LINES_PER_PAGE === 0 && line.trim() === '') continue; // 页间空行
|
||||
codeLines.push(line);
|
||||
count++;
|
||||
}
|
||||
return codeLines;
|
||||
}
|
||||
|
||||
async function buildSourceDocx() {
|
||||
const codeLines = parseSourceTxt();
|
||||
|
||||
// 折行后按每页 50 个显示行分页
|
||||
const folded = codeLines.flatMap((l) => foldLine(l.replace(/\t/g, ' ')));
|
||||
const totalPages = Math.ceil(folded.length / LINES_PER_PAGE);
|
||||
const foldedPages = Math.ceil(codeLines.length / LINES_PER_PAGE); // 折行前理论页数(对照 txt)
|
||||
|
||||
const children = folded.map((line, i) =>
|
||||
new Paragraph({
|
||||
pageBreakBefore: i > 0 && i % LINES_PER_PAGE === 0,
|
||||
spacing: { after: 0, line: 230, lineRule: 'auto' },
|
||||
children: [
|
||||
new TextRun({
|
||||
text: line,
|
||||
size: 19, // 9.5pt
|
||||
font: { ascii: 'Consolas', eastAsia: 'SimSun' },
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
const doc = new Document({
|
||||
sections: [{
|
||||
properties: { page: PAGE },
|
||||
headers: { default: makeHeader() },
|
||||
children,
|
||||
}],
|
||||
});
|
||||
|
||||
await Packer.toBuffer(doc).then((buf) => fs.writeFileSync(OUT_SRC, buf));
|
||||
|
||||
console.log('=== 源程序 docx 生成完成 ===');
|
||||
console.log(`代码行: ${codeLines.length},折行后显示行: ${folded.length}`);
|
||||
console.log(`折行前页数(对照txt): ${foldedPages},docx 实际页数: ${totalPages}`);
|
||||
console.log(`输出: ${OUT_SRC}`);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 二、设计说明书 docx
|
||||
// ============================================================
|
||||
|
||||
const BODY_FONT = { ascii: 'Times New Roman', eastAsia: 'SimSun' };
|
||||
const HEAD_FONT = { ascii: 'Arial', eastAsia: 'SimHei' };
|
||||
|
||||
/** 解析 **粗体** 行内标记 */
|
||||
function parseRuns(text, base = {}) {
|
||||
return text
|
||||
.split(/(\*\*[^*]+\*\*)/g)
|
||||
.filter(Boolean)
|
||||
.map((p) =>
|
||||
p.startsWith('**')
|
||||
? new TextRun({ text: p.slice(2, -2), bold: true, font: BODY_FONT, ...base })
|
||||
: new TextRun({ text: p, font: BODY_FONT, ...base })
|
||||
);
|
||||
}
|
||||
|
||||
async function buildManualDocx() {
|
||||
const md = fs.readFileSync(MANUAL_MD, 'utf8').split('\n');
|
||||
const children = [];
|
||||
let coverDone = false;
|
||||
|
||||
for (const rawLine of md) {
|
||||
const line = rawLine.trimEnd();
|
||||
|
||||
if (!coverDone) {
|
||||
// 封面区:软件名 / 文档名 / 著作权人 / 日期,居中排布,遇 --- 结束封面
|
||||
if (line.startsWith('# ')) {
|
||||
children.push(new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
spacing: { before: 240, after: 240 },
|
||||
children: [new TextRun({ text: line.slice(2), bold: true, size: 52, font: HEAD_FONT })],
|
||||
}));
|
||||
} else if (/^(著作权人|开发完成日期):/.test(line)) {
|
||||
children.push(new Paragraph({
|
||||
alignment: AlignmentType.CENTER,
|
||||
spacing: { before: 120, after: 120 },
|
||||
children: [new TextRun({ text: line, size: 24, font: BODY_FONT })],
|
||||
}));
|
||||
} else if (line.trim() === '---') {
|
||||
coverDone = true;
|
||||
// 封面独立成页
|
||||
children.push(new Paragraph({ children: [new TextRun({ text: '', size: 2 })], pageBreakBefore: false }));
|
||||
children.push(new Paragraph({ pageBreakBefore: true, children: [] }));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 正文区
|
||||
if (line.trim() === '' || line.trim() === '---') continue;
|
||||
if (line.startsWith('## ')) {
|
||||
children.push(new Paragraph({
|
||||
heading: HeadingLevel.HEADING_1,
|
||||
spacing: { before: 240, after: 120 },
|
||||
children: [new TextRun({ text: line.slice(3), bold: true, size: 30, font: HEAD_FONT, color: '000000' })],
|
||||
}));
|
||||
} else if (line.startsWith('### ')) {
|
||||
children.push(new Paragraph({
|
||||
heading: HeadingLevel.HEADING_2,
|
||||
spacing: { before: 180, after: 100 },
|
||||
children: [new TextRun({ text: line.slice(4), bold: true, size: 26, font: HEAD_FONT, color: '000000' })],
|
||||
}));
|
||||
} else if (line.startsWith('【建议插入截图')) {
|
||||
children.push(new Paragraph({
|
||||
spacing: { before: 60, after: 60 },
|
||||
indent: { left: 420 },
|
||||
children: [new TextRun({ text: line, italics: true, color: '808080', size: 21, font: BODY_FONT })],
|
||||
}));
|
||||
} else if (/^\d+\.\s/.test(line)) {
|
||||
children.push(new Paragraph({
|
||||
spacing: { after: 100 },
|
||||
indent: { left: 420 },
|
||||
children: parseRuns(line, { size: 21 }),
|
||||
}));
|
||||
} else {
|
||||
children.push(new Paragraph({
|
||||
spacing: { after: 100, line: 300 },
|
||||
children: parseRuns(line, { size: 21 }),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
const doc = new Document({
|
||||
styles: {
|
||||
default: {
|
||||
document: { run: { font: BODY_FONT, size: 21 } },
|
||||
},
|
||||
},
|
||||
sections: [{
|
||||
properties: { page: PAGE },
|
||||
headers: { default: makeHeader() },
|
||||
children,
|
||||
}],
|
||||
});
|
||||
|
||||
await Packer.toBuffer(doc).then((buf) => fs.writeFileSync(OUT_MANUAL, buf));
|
||||
console.log('=== 设计说明书 docx 生成完成 ===');
|
||||
console.log(`输出: ${OUT_MANUAL}`);
|
||||
}
|
||||
|
||||
await buildSourceDocx();
|
||||
await buildManualDocx();
|
||||
111
docs/软著/gen_source_doc.mjs
Normal file
111
docs/软著/gen_source_doc.mjs
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 软著源程序文档生成脚本
|
||||
* 规则:每页 60 行有效代码;超过 60 页时取前 30 页 + 后 30 页;
|
||||
* 自动清理文件头第三方署名/版权行;排除声明文件与空文件。
|
||||
* 用法:node docs/软著/gen_source_doc.mjs
|
||||
*/
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dirname, '../../assets/script');
|
||||
const OUT_DIR = import.meta.dirname;
|
||||
const OUT = path.join(OUT_DIR, '像素大勇者游戏软件_V1.0_源程序.txt');
|
||||
const LINES_PER_PAGE = 60;
|
||||
const KEEP = 30; // 前 30 页 + 后 30 页
|
||||
const SOFTWARE = '像素大勇者游戏软件 V1.0';
|
||||
|
||||
/** 需要从文件头清除的第三方署名 / 编辑器元数据行 */
|
||||
const BAD_PATTERNS = [
|
||||
/dgflash/i,
|
||||
/落日故人/,
|
||||
/QQ\s*\d+/,
|
||||
/Copyright \(c\)/i,
|
||||
/@(Author|LastEditors?|Date|LastEditTime)/i,
|
||||
];
|
||||
|
||||
function walk(dir) {
|
||||
const out = [];
|
||||
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) out.push(...walk(p));
|
||||
else out.push(p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const rel = (f) => path.relative(ROOT, f).replace(/\\/g, '/');
|
||||
|
||||
// 1. 收集源文件:仅 .ts,排除 .d.ts 声明文件、空文件、非脚本目录
|
||||
let files = walk(ROOT).filter(
|
||||
(f) =>
|
||||
f.endsWith('.ts') &&
|
||||
!f.endsWith('.d.ts') &&
|
||||
!rel(f).includes('hit-flash-white/shaders') &&
|
||||
fs.readFileSync(f, 'utf8').trim().length > 0
|
||||
);
|
||||
|
||||
// 2. 排序:入口 Main.ts 优先,其次初始化模块,其余按路径字母序
|
||||
files.sort((a, b) => {
|
||||
const rank = (r) =>
|
||||
r === 'Main.ts' ? 0 : r.startsWith('game/initialize') ? 1 : 2;
|
||||
return rank(rel(a)) - rank(rel(b)) || rel(a).localeCompare(rel(b));
|
||||
});
|
||||
|
||||
// 3. 清理文件头(前 40 行)中的第三方署名行,统一换行符与缩进
|
||||
function cleanFile(content) {
|
||||
const lines = content
|
||||
.replace(/\r\n/g, '\n')
|
||||
.replace(/\t/g, ' ')
|
||||
.split('\n');
|
||||
const head = lines.slice(0, 40).filter(
|
||||
(l) => !BAD_PATTERNS.some((rx) => rx.test(l))
|
||||
);
|
||||
return [...head, ...lines.slice(40)];
|
||||
}
|
||||
|
||||
const all = [];
|
||||
for (const f of files) {
|
||||
all.push(`// ===================== 文件: ${rel(f)} =====================`);
|
||||
all.push(...cleanFile(fs.readFileSync(f, 'utf8')));
|
||||
}
|
||||
|
||||
// 4. 统计(审校用)
|
||||
const total = all.length;
|
||||
const blank = all.filter((l) => l.trim() === '').length;
|
||||
const comment = all.filter((l) => /^\s*(\/\/|\/\*|\*|\*\/)/.test(l)).length;
|
||||
const tooLong = all.filter((l) => l.length > 84).length;
|
||||
const totalPages = Math.ceil(total / LINES_PER_PAGE);
|
||||
|
||||
// 5. 分页:超过 60 页取前 30 + 后 30
|
||||
const pageIdx =
|
||||
totalPages > 60
|
||||
? [
|
||||
...Array.from({ length: KEEP }, (_, i) => i),
|
||||
...Array.from({ length: KEEP }, (_, i) => totalPages - KEEP + i),
|
||||
]
|
||||
: Array.from({ length: totalPages }, (_, i) => i);
|
||||
const submitPages = pageIdx.length;
|
||||
|
||||
// 6. 生成文档(页眉:左侧软件全称+版本号,右侧页码)
|
||||
const out = [];
|
||||
for (const pi of pageIdx) {
|
||||
const pageNo = pageIdx.indexOf(pi) + 1;
|
||||
const start = pi * LINES_PER_PAGE;
|
||||
const body = all.slice(start, start + LINES_PER_PAGE);
|
||||
out.push(`${SOFTWARE} 第 ${pageNo} 页 共 ${submitPages} 页`);
|
||||
out.push('='.repeat(76));
|
||||
out.push(...body);
|
||||
out.push('');
|
||||
}
|
||||
|
||||
fs.mkdirSync(OUT_DIR, { recursive: true });
|
||||
fs.writeFileSync(OUT, out.join('\n'), 'utf8');
|
||||
|
||||
console.log('=== 源程序文档生成完成 ===');
|
||||
console.log(`文件数: ${files.length}`);
|
||||
console.log(`总行数: ${total}(含文件分隔行)`);
|
||||
console.log(`总页数: ${totalPages},提交页数: ${submitPages}`);
|
||||
console.log(`空行占比: ${((blank / total) * 100).toFixed(1)}%`);
|
||||
console.log(`注释行占比: ${((comment / total) * 100).toFixed(1)}%`);
|
||||
console.log(`超 84 字符行数: ${tooLong}`);
|
||||
console.log(`输出: ${OUT}`);
|
||||
207
docs/软著/package-lock.json
generated
Normal file
207
docs/软著/package-lock.json
generated
Normal file
@@ -0,0 +1,207 @@
|
||||
{
|
||||
"name": "copyright-registration-docs",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "copyright-registration-docs",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"docx": "^9.7.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.9.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.6.tgz",
|
||||
"integrity": "sha512-JR6Q/PV5DKFvjrGFVqQJdeG0qvsqQQLDa3TzFrqVwhqRXqwNaxPo2KYCtCQXpOdIulCouKwT7a6in9nFthBAzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": ">=7.24.0 <7.24.7"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/docx": {
|
||||
"version": "9.7.1",
|
||||
"resolved": "https://registry.npmjs.org/docx/-/docx-9.7.1.tgz",
|
||||
"integrity": "sha512-ilXFf9Moz47ABjFpDiA5s1w9lpb4EFSp7+5iiJSbfyYDM+bpZdAgLlSr7fW4aXhVe/E+F6QCv0EvRVFEd5CsWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "^25.2.3",
|
||||
"hash.js": "^1.1.7",
|
||||
"jszip": "^3.10.1",
|
||||
"nanoid": "^5.1.3",
|
||||
"xml": "^1.0.1",
|
||||
"xml-js": "^1.6.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/hash.js": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"minimalistic-assert": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jszip": {
|
||||
"version": "3.10.2",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.2.tgz",
|
||||
"integrity": "sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==",
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"dependencies": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"setimmediate": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/minimalistic-assert": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "5.1.16",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz",
|
||||
"integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
|
||||
"integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
|
||||
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/xml": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
|
||||
"integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/xml-js": {
|
||||
"version": "1.6.11",
|
||||
"resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz",
|
||||
"integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"bin": {
|
||||
"xml-js": "bin/cli.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
docs/软著/package.json
Normal file
14
docs/软著/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "copyright-registration-docs",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "软著登记材料生成工具(独立于游戏项目依赖)",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"src": "node gen_source_doc.mjs",
|
||||
"docx": "node gen_docx.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"docx": "^9.7.1"
|
||||
}
|
||||
}
|
||||
169
docs/软著/像素大勇者游戏软件_V1.0_填表指引.md
Normal file
169
docs/软著/像素大勇者游戏软件_V1.0_填表指引.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# 像素大勇者游戏软件 V1.0 —— 软著登记填表指引
|
||||
|
||||
> 本指引配合《登记申请表》填报速查表使用,按中国版权保护中心在线系统的实际填报流程逐项说明。
|
||||
|
||||
---
|
||||
|
||||
## 一、办理前准备
|
||||
|
||||
| 材料 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 软件全称/版本号 | ✅ 已定 | 像素大勇者游戏软件 V1.0(三份材料必须完全一致) |
|
||||
| 源程序鉴别材料 | ✅ 已生成 | `像素大勇者游戏软件_V1.0_源程序.docx`(60 页,每页 60 行) |
|
||||
| 文档鉴别材料 | ✅ 已生成 | `像素大勇者游戏软件_V1.0_设计说明书.docx`(补个人信息与截图后定稿) |
|
||||
| 身份证正反面扫描件 | ⏳ 自备 | 个人申请必需,jpg/pdf 均可,需清晰完整 |
|
||||
| 实名认证 | ⏳ 自办 | 注册账号后需完成个人实名认证(人脸/银行卡)方可提交 |
|
||||
|
||||
**办理入口**:中国版权保护中心 https://www.ccopyright.com.cn → 计算机软件著作权登记申请(目前不收取登记费用)。
|
||||
|
||||
---
|
||||
|
||||
## 二、在线填报流程
|
||||
|
||||
1. 注册并登录中国版权保护中心账号,完成**个人实名认证**(1 个工作日内审核);
|
||||
2. 进入「软件著作权登记申请」→「新增申请」;
|
||||
3. 按下方第三节逐项填写申请表;
|
||||
4. 上传鉴别材料 PDF(源程序 + 设计说明书)与身份证扫描件;
|
||||
5. 提交后等待受理通知(一般 1-5 个工作日出受理号,可在线查看进度)。
|
||||
|
||||
---
|
||||
|
||||
## 三、申请表逐项填报对照
|
||||
|
||||
### 3.1 软件基本信息
|
||||
|
||||
| 系统字段 | 填写内容 | 注意事项 |
|
||||
|----------|---------|---------|
|
||||
| 软件全称 | 像素大勇者游戏软件 | 必须与页眉、说明书封面完全一致,一字不差 |
|
||||
| 软件简称 | 像素大勇者 | 可不填;若填写需与全称有包含关系 |
|
||||
| 版本号 | V1.0 | 默认即可;已上线软件勿填上线版本以外的新版本号 |
|
||||
| 软件分类 | 游戏软件 | 在下拉选项中选择 |
|
||||
| 开发完成日期 | 2026-09-13 | 必须早于等于申请日期 |
|
||||
| 发表状态 | 未发表 | 已发表则需填首次发表日期(精确到日)与发表地点 |
|
||||
| 开发方式 | 独立开发 | 个人独立开发,权利归属清晰 |
|
||||
| 权利取得方式 | 原始取得 | 自行开发取得的原始权利 |
|
||||
| 权利范围 | 全部权利 | 默认全部 |
|
||||
|
||||
### 3.2 著作权人信息(个人)
|
||||
|
||||
| 系统字段 | 填写内容 | 注意事项 |
|
||||
|----------|---------|---------|
|
||||
| 姓名 | ___ | **必须与身份证姓名完全一致**,不能用曾用名/昵称 |
|
||||
| 证件类型 | 居民身份证 | — |
|
||||
| 证件号码 | ___ | 提交前核对位数 |
|
||||
| 国籍 | 中国 | — |
|
||||
| 地址 | ___ | 建议与身份证地址一致;填写实际居住地址亦可,但需可收信 |
|
||||
| 邮编 | ___ | 与地址匹配 |
|
||||
| 联系电话 | ___ | 保持畅通,受理通知可能电话联系 |
|
||||
| 电子邮箱 | ___ | 用于接收电子通知书 |
|
||||
|
||||
> ⚠️ 著作权人姓名须与平台实名认证主体一致,否则无法提交。
|
||||
|
||||
### 3.3 软件开发信息(12 个字段逐项指引)
|
||||
|
||||
| # | 系统字段 | 填写内容(可直接粘贴) | 填写要点 |
|
||||
|---|----------|----------------------|---------|
|
||||
| 1 | 开发的硬件环境 | CPU Intel Core i5 2.4GHz 及以上;内存 16GB 及以上;硬盘 256GB 及以上;支持联网 | 指开发时使用的电脑配置,按实际开发机填写 |
|
||||
| 2 | 运行的硬件环境 | CPU 1.2GHz 及以上;内存 2GB 及以上;存储空间 200MB 及以上;支持联网 | 指软件运行的目标设备(手机),要求写低一些以扩大兼容范围 |
|
||||
| 3 | 开发该软件的操作系统 | Windows 10 / Windows 11(64 位) | 按实际开发机系统填写 |
|
||||
| 4 | 软件开发环境 / 开发工具 | Cocos Creator 3.8.6 游戏引擎编辑器、Visual Studio Code 代码编辑器、微信开发者工具 | 逐个列出主要工具即可,不必穷举 |
|
||||
| 5 | 该软件的运行平台 / 操作系统 | Android 8.0 及以上;iOS 12 及以上 | 指软件部署运行的目标系统,非开发机系统,勿与第 3 项混淆 |
|
||||
| 6 | 软件运行支撑环境 / 支持软件 | 微信客户端 8.0 及以上(微信小游戏运行时);微信云开发服务(登录与云存档) | 软件正常运行所依赖的第三方支撑软件 |
|
||||
| 7 | 编程语言 | TypeScript | 主语言;仅一种时直接填写,勿把 JSON/配置格式算作语言 |
|
||||
| 8 | 源程序量 | 约 30000 行 | 与提交的源程序材料逻辑一致(全部代码 30304 行,提交前后各 30 页截取) |
|
||||
| 9 | 开发目的 | 本软件为个人独立开发的休闲益智类手机游戏,旨在为玩家提供策略卡牌与自动战斗相结合的休闲娱乐体验,并通过著作权登记明确作品权属、保护开发者合法权益 | 如实填写,不夸大商业目标 |
|
||||
| 10 | 面向领域 / 行业 | 文化、体育和娱乐业(网络游戏/休闲娱乐) | 按系统下拉选项就近选择,游戏软件通常归入文化娱乐类 |
|
||||
| 11 | 软件的主要功能 | 见下方「可直接粘贴文案」 | 500 字以上(系统要求),覆盖各功能模块,用"玩家/关卡/战斗/卡牌"等游戏术语,书面语 |
|
||||
| 12 | 软件的技术特点 | 见下方「可直接粘贴文案」 | 150-250 字,突出架构与技术实现,不出现竞品品牌名 |
|
||||
|
||||
> ⚠️ 易错提示:**第 1/2 项**(开发 vs 运行硬件)、**第 3/5 项**(开发 vs 运行操作系统)是两对容易填反的字段,审查员会核对逻辑一致性。
|
||||
|
||||
**主要功能(直接粘贴,约 610 字):**
|
||||
|
||||
本软件是一款像素风格的卡牌策略自走战斗类手机游戏软件,运行于微信小游戏平台,为玩家提供策略构建与自动战斗相结合的休闲娱乐体验。
|
||||
玩家进入任务主页后点击开始即可开启一局游戏。开局阶段,玩家通过三次"三选一"卡牌召唤从英雄卡池中抽取并组建三人英雄小队,卡池按权重配置抽取并自动去重,玩家可消耗局内金币刷新卡池以获取更优组合。准备阶段,玩家可在技能商店与道具商店购买技能卡、装备卡与药品卡,构建本局战斗组合,形成差异化的养成路线。
|
||||
战斗阶段采用自动战斗机制,英雄与怪物在网格站位上自动移动、索敌、攻击与施法,玩家无需手动操作即可观赏完整战斗过程。技能系统支持即时、定时、驻场、事件四类触发时机,配合增益与减益效果的挂载、叠加与到期移除,通过统一伤害队列结算并显示伤害数字,形成丰富的战斗策略与表现。战斗表现层运用骨骼动画、帧动画与震屏、受击闪白等特效强化打击反馈。
|
||||
关卡采用"章节—关—节"三级结构,每关包含五个小节,前三节为普通战斗,第四节为精英战斗,第五节为首领战斗,节间自动连战,通关后进入下一关。战斗结束进入结算,展示积分段位评定、段位徽章、总分滚动、亮点成就与奖励条目,玩家可获得英雄碎片等奖励,并在排行榜中查看排名。
|
||||
局外养成方面,玩家可使用英雄碎片升级英雄属性,并在基础、专精、传说三阶天赋树中加点强化全局战斗能力,天赋加点支持消耗校验、阶位解锁与重置。软件另提供中英文双语界面、云端存档同步(本地与云端双向比对取新)、连杀统计与成就展示等功能,形成"战斗—养成—再战斗"的完整游戏循环。
|
||||
|
||||
**技术特点(直接粘贴):**
|
||||
|
||||
本软件基于 Cocos Creator 引擎与 TypeScript 语言开发,采用 ECS(实体-组件-系统)架构管理战斗实体,英雄、技能、增益效果均以实体形式注册与调度,提升大量同类对象的运行效率。界面采用数据模型、视图、组件分层与数据绑定模式,模块间通过全局事件总线解耦通信。游戏数据由单例模块统一管理,通过微信云开发接口实现登录与本地、云端双向存档同步,并以数据加密保障存档安全。战斗表现层综合运用骨骼动画、帧动画图集、补间动画与自定义着色器特效,实现受击闪白、震屏等打击反馈。
|
||||
|
||||
---
|
||||
|
||||
## 四、鉴别材料上传要求
|
||||
|
||||
### 4.1 源程序(像素大勇者游戏软件_V1.0_源程序.docx → 另存为 PDF)
|
||||
|
||||
- 共 60 页 = 前 30 页 + 后 30 页,每页 60 行(≥50 行的规范要求)
|
||||
- 页眉已含:软件全称 + 版本号 + 自动页码
|
||||
- **上传前**:打开检查首页/末页代码完整,另存为 PDF 后上传
|
||||
|
||||
### 4.2 设计说明书(像素大勇者游戏软件_V1.0_设计说明书.docx → 另存为 PDF)
|
||||
|
||||
- 上传前必须完成:
|
||||
1. 封面著作权人处填本人姓名
|
||||
2. 文末联系方式补电话/邮箱
|
||||
3. 9 处【建议插入截图】补齐游戏截图(共 5-10 张,截图后删除灰色提示文字)
|
||||
|
||||
### 4.3 文件命名
|
||||
|
||||
上传文件建议统一命名(系统对文件名有要求,含软件全称更清晰):
|
||||
|
||||
```
|
||||
像素大勇者游戏软件_V1.0_源程序.pdf
|
||||
像素大勇者游戏软件_V1.0_设计说明书.pdf
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、提交前自检清单
|
||||
|
||||
- [ ] 软件全称在「申请表 / 源程序页眉 / 说明书封面」三处完全一致
|
||||
- [ ] 版本号 V1.0 三处一致
|
||||
- [ ] 著作权人姓名与身份证、平台实名认证三方一致
|
||||
- [ ] 源程序量(约 30000 行)与实际材料页数逻辑匹配(60 页 × 60 行 ≈ 3600 行提交行数,为全部代码的前后截取,总代码 30304 行)
|
||||
- [ ] 说明书已补截图与个人信息,无【建议插入截图】残留文字
|
||||
- [ ] 身份证扫描件清晰、在有效期内
|
||||
- [ ] PDF 均能正常打开、页码连续
|
||||
|
||||
---
|
||||
|
||||
## 六、常见退回原因与规避
|
||||
|
||||
| 退回原因 | 规避方式 |
|
||||
|---------|---------|
|
||||
| 三处名称/版本不一致 | 提交前逐字核对 |
|
||||
| 源程序含第三方版权声明 | 本项目已清理 dgflash 署名、他人 QQ、引擎版权行 ✅ |
|
||||
| 源程序空行/注释占比过高 | 本项目空行 10.7%、注释 26.3%,均低于红线 ✅ |
|
||||
| 说明书出现与著作权人无关的第三方品牌 | 本项目说明书正文已泛化处理 ✅ |
|
||||
| 材料为截图/拍照件而非文档 | 上传规范 PDF,不用照片 |
|
||||
| 开发完成日期晚于申请日 | 已填 2026-09-13,早于申请日 ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 七、时间线参考
|
||||
|
||||
| 环节 | 参考时长 |
|
||||
|------|---------|
|
||||
| 实名认证 | 1 个工作日 |
|
||||
| 受理通知(发放受理号) | 提交后 1-5 个工作日 |
|
||||
| 审查(普通通道) | 受理后约 30-60 个工作日 |
|
||||
| 领取证书 | 审查通过后可选择电子证书或邮寄纸质证书 |
|
||||
|
||||
> 加急通道需通过代理机构办理(官方本身免费),个人自办走普通通道即可。
|
||||
|
||||
---
|
||||
|
||||
## 八、材料更新方式
|
||||
|
||||
游戏代码更新后,重新生成源程序与说明书:
|
||||
|
||||
```bash
|
||||
cd docs/软著
|
||||
node gen_source_doc.mjs # 重新提取代码生成 txt
|
||||
node gen_docx.mjs # 生成/更新两个 docx(需先关闭 Word 中打开的文件)
|
||||
```
|
||||
|
||||
生成脚本:`gen_source_doc.mjs`(每页 60 行)、`gen_docx.mjs`(docx 排版),依赖独立于游戏项目。
|
||||
BIN
docs/软著/像素大勇者游戏软件_V1.0_源程序.docx
Normal file
BIN
docs/软著/像素大勇者游戏软件_V1.0_源程序.docx
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user