20 Commits

Author SHA1 Message Date
pan
e1c37eb510 docs(config): add meta files for kingdom skill config docs
add metadata files for the two newly added configuration documents about geometric kingdom skills
2026-08-14 17:31:14 +08:00
pan
2d11246838 refactor: 移除战斗结束相关的FEnd触发逻辑
此次提交全面清理了所有与战斗结束(FEnd)相关的代码:
1. 移除了Hero、Monster等实体中的fend属性与运行时处理
2. 删除了SkillTriggerHelper中的FEnd触发分支
3. 移除了GameEvent中的FightEnd事件与相关监听
4. 清理了配置文件中的FEnd相关枚举、字段与装备
5. 重构了MissionComp的回合逻辑,不再触发战后技能
6. 更新了注释与文档,移除过时的FEnd相关说明

彻底移除了已不再使用的战斗结束触发机制,简化了代码架构。
2026-08-14 15:28:22 +08:00
pan
d759689269 docs: 新增《几何王国》英雄技能汇总配置文档
该文档收录了游戏主流热门英雄的技能、养成解锁条件、觉醒技能、强度排行等内容,整理自多个公开玩家攻略资源,方便团队查阅参考。
2026-08-14 10:58:25 +08:00
pan
e78f4365e7 refactor(heroSet): 重构英雄攻速配置体系
将原有的18级3档攻速配置简化为5档统一配置,更新所有英雄和怪物的技能攻速引用,调整怪物基准属性说明以匹配新的攻速配置。
2026-08-14 10:18:35 +08:00
pan
34a62f0a87 fix(config): adjust map boundary values
modify LEFT_END and RIGHT_END from ±360 to ±320 to correct map border range
2026-08-14 10:00:32 +08:00
pan
1988ae5ef1 refactor(hero): 统一击退位移逻辑到MoveSystem
将英雄后退的视图层位移操作迁移到MoveSystem统一处理,
通过knockbackVelocity组件属性实现平滑的击退位移衰减与边界钳制,
移除HeroViewComp中直接操作节点位置的代码,解耦视图与战斗逻辑。
2026-08-14 09:53:05 +08:00
panFD
df0dedba75 refactor: 整理技能与战斗相关的配置与逻辑
1.  新增技能飞行距离配置项,支持自定义直线技能飞行距离
2.  调整英雄后退击退范围从30改为20
3.  重构击退逻辑,统一敌我双方的边界钳制处理
4.  优化Boss数值配置,固定基础面板为初始英雄2倍强度
5.  修正技能6006的动画资源引用为atk1
6.  调整近战英雄默认停火距离从80改为60
7.  更新注释文档与配置注释,优化代码可读性
2026-08-14 00:36:26 +08:00
panFD
4fee02d82e refactor(hero): 移除英雄等级显示相关逻辑
1. 从HeroTopBarComp中移除等级Label组件、lvShow方法以及等级显示调用
2. 从HeroViewComp的update逻辑中移除等级脏数据监听
3. 调整英雄顶栏预制体的布局参数并隐藏等级节点
2026-08-13 23:16:44 +08:00
panFD
15d14f7d59 refactor(hero&monster): 调整英雄与怪物的攻速配置与近战停止距离
1.  全局重构攻击攻速分级配置,统一放缓慢攻速档位上限为1.10s以紧凑战斗节奏
2.  调整近战单位停止追击距离从60提升至80,优化近战英雄作战手感
3.  批量更新所有英雄与怪物的技能攻击间隔,匹配新的攻速配置
2026-08-13 23:07:09 +08:00
panFD
9579c4f75e refactor(monster): 重构怪物分层显示与战斗逻辑
1.  重构怪物挂载逻辑,按Boss/小怪分别挂载到对应场景层
2.  调整怪物出生落点逻辑,移除额外Y轴偏移
3.  优化场景节点清理范围,新增MON/BOSS层清理
4.  重构渲染排序逻辑,分层管理怪物渲染层级
5.  新增5套攻击技能预制体与动画资源
6.  修复技能配置参数冗余注释与类型错误
2026-08-13 23:05:51 +08:00
pan
d54e6ce1d6 fix: 调整游戏地平线高度与UI、场景位置
统一修正游戏内角色控制器UI位置、地图物体Y轴偏移
调整游戏地平线基准值,修复场景显示偏移问题
2026-08-13 17:29:53 +08:00
pan
d06c99e2f1 refactor(hero): 重构英雄头顶状态栏逻辑到独立组件
将原HeroViewComp中的头顶血条、等级、护盾UI逻辑抽离为HeroTopBarComp,
统一管理头顶状态栏的初始化、数据更新、动画和显隐逻辑,减少HeroViewComp代码冗余,
提升代码可维护性和复用性。同时更新预制体关联,适配新的组件结构。
2026-08-13 17:20:38 +08:00
pan
76082d8e1f refactor: 重构护盾机制为数值减免型
1. 调整护盾逻辑:从单次抵挡改为按伤害量减免
2. 更新配置、注释与UI提示文本,统一术语表述
3. 修复prefab布局适配新的护盾显示样式
2026-08-13 16:53:15 +08:00
pan
695a9e2b66 refactor(skill): 重构技能配置加载逻辑,将参数下沉至prefab配置
1. 新增SkillView组件属性runType/endType/speed/time,替换原配置表字段
2. 批量同步所有技能prefab的配置参数
3. 优化技能运行逻辑,优先使用prefab配置,配置表仅作兜底
4. 新增同步脚本用于批量迁移技能配置
2026-08-13 16:26:19 +08:00
pan
c6b5e1431f fix(skill): 修复技能相关的显示与逻辑问题
1. 调整技能预制体的锚点和攻击偏移参数
2. 修复SkillView.ts的代码格式与导入错误
3. 补充fixed类型技能的动画结束销毁逻辑
2026-08-13 15:36:57 +08:00
pan
7fa18329d6 fix(skill): 调整普攻技能配置与表现
1.  修改普攻预制体的缩放、尺寸、锚点与精灵贴图
2.  更新普攻动画的采样率、时长与帧动画帧
3.  调整技能配置,将默认普攻替换为6006并修正攻击类型
4.  统一全英雄与怪物的默认技能为6006普攻
2026-08-13 15:11:11 +08:00
panFD
a2c397120c docs(skill): 更新技能4的图标资源
替换了assets/resources/game/skill/skill4.png的图片资源
2026-08-13 14:22:41 +08:00
panFD
cb2a72da52 refactor(map): 重构rogue战斗数值生成逻辑
重构了Rogue模式的怪物数值计算方式,移除了基于英雄实时强度的动态缩放算法,改为统一使用基础属性乘以可调系数的固定缩放方案:
1.  更新了怪物配置基础属性,对齐1级英雄面板作为基准
2.  简化了数值公式,仅保留三个可调节的全局系数用于平衡难度
3.  删除了冗余的ECS查询、强度计算和类型系数逻辑
4.  更新了全部注释文档,明确数值校准目标和调整方式
2026-08-13 14:12:05 +08:00
pan
9da3e2d9fd fix(map): adjust global monster power multiplier to 1
更新全局怪物强度系数配置,将原本的2/3调整为1,让怪物强度和英雄能力持平
2026-08-13 11:17:29 +08:00
pan
eb4b370533 refactor: 重构击退机制,将概率改为距离增量
1. 调整近战默认攻击距离从150改为100
2. 将击退相关属性从概率改为击退距离增量,同步更新所有配置、UI显示和技能描述
3. 移除旧的击退概率判定逻辑,改为直接应用击退距离增量
4. 优化受击击退的处理流程,统一参数传递
2026-08-13 11:09:37 +08:00
81 changed files with 9338 additions and 1167 deletions

View File

@@ -38,10 +38,13 @@
"_components": [
{
"__id__": 110
},
{
"__id__": 112
}
],
"_prefab": {
"__id__": 112
"__id__": 114
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -95,7 +98,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 9.114,
"x": 0,
"y": 10,
"z": 0
},
@@ -217,7 +220,7 @@
"__id__": 17
}
],
"_active": false,
"_active": true,
"_components": [
{
"__id__": 25
@@ -235,7 +238,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": -9.738,
"y": -7.84,
"z": 0
},
"_lrot": {
@@ -287,7 +290,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -35,
"x": -20,
"y": 0,
"z": 0
},
@@ -328,8 +331,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 70,
"height": 10
"width": 40,
"height": 5
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -506,8 +509,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 240,
"height": 40
"width": 140,
"height": 23.333333333333336
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -631,8 +634,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 70,
"height": 10
"width": 40,
"height": 5
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -859,7 +862,7 @@
"_contentSize": {
"__type__": "cc.Size",
"width": 40,
"height": 11
"height": 9
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1037,7 +1040,7 @@
"_contentSize": {
"__type__": "cc.Size",
"width": 140,
"height": 43.333333333333336
"height": 36.66666666666667
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1162,7 +1165,7 @@
"_contentSize": {
"__type__": "cc.Size",
"width": 40,
"height": 11
"height": 9
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1347,7 +1350,7 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -25,
"x": -20,
"y": 0,
"z": 0
},
@@ -1388,8 +1391,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 50,
"height": 10
"width": 40,
"height": 9
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1691,8 +1694,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 50,
"height": 10
"width": 40,
"height": 9
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1813,7 +1816,7 @@
"__id__": 99
}
],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 105
@@ -2520,8 +2523,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 70,
"height": 20
"width": 40,
"height": 11
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -2534,6 +2537,33 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "cdvV5hAkdEaY6AT6urHKuf"
},
{
"__type__": "c4181/FEWhKbY0D0PLdCyKi",
"_name": "",
"_objFlags": 0,
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 113
},
"hpProgressBar": {
"__id__": 53
},
"lvLabel": null,
"shieldProgressBar": {
"__id__": 77
},
"hpBarSprite": {
"__id__": 36
},
"_id": "a77QVRkP5NX4b+LyqmW1KJ"
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "3eCKBFOJhLHKqcnak/mi3d"
},
{
"__type__": "cc.PrefabInfo",
"root": {

View File

@@ -1398,7 +1398,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 21.504,
"y": -31.947,
"z": 0
},
"_lrot": {
@@ -1875,7 +1875,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 21.504,
"y": -50.813,
"z": 0
},
"_lrot": {
@@ -2352,7 +2352,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"y": -150,
"z": 0
},
"_lrot": {
@@ -2618,6 +2618,8 @@
"__id__": 0
},
"fileId": "6bE0ieRMBGyYHdxvuJ03FA",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -3432,6 +3434,8 @@
"__id__": 0
},
"fileId": "256TxMVzhHQopA08O7v8nq",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -3471,6 +3475,8 @@
"__id__": 0
},
"fileId": "5dWwAe3IFHRJuoSicXBEs8",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -3710,6 +3716,8 @@
"__id__": 0
},
"fileId": "b9zLdCeJlE5pPiK++dLgBX",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -4388,6 +4396,8 @@
"__id__": 0
},
"fileId": "9d2p/canpFGof2O/w8JyTj",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -4427,6 +4437,8 @@
"__id__": 0
},
"fileId": "6bkL20JyRDOI6ycVYKl/ie",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -4495,6 +4507,8 @@
"__id__": 0
},
"fileId": "14GnCNU65BbJPIzLWE/Oiz",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -7363,6 +7377,8 @@
"__id__": 0
},
"fileId": "c4IqMKgXNExa5w4EAciRxE",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -8202,7 +8218,7 @@
},
{
"__type__": "cc.Node",
"_name": "LINE2",
"_name": "BOSS",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8273,7 +8289,7 @@
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ea767WaZRHo4s0bZQfN8zP"
"fileId": "3fVB8r2UFAB5QujD6LOw6s"
},
{
"__type__": "cc.PrefabInfo",
@@ -8283,14 +8299,14 @@
"asset": {
"__id__": 0
},
"fileId": "60UdPtJn9ALLHCveLwT1bT",
"fileId": "f4iFaXCXNKorfGJFH32POa",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "HERO",
"_name": "MON",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8361,7 +8377,7 @@
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "dasmpuTSVM7qA3QW9/3XjQ"
"fileId": "ea767WaZRHo4s0bZQfN8zP"
},
{
"__type__": "cc.PrefabInfo",
@@ -8371,14 +8387,14 @@
"asset": {
"__id__": 0
},
"fileId": "eaiasKNEFO6qvodGhL4a0b",
"fileId": "60UdPtJn9ALLHCveLwT1bT",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "LINE1",
"_name": "HERO",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8449,7 +8465,7 @@
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "b8C5CFg5lM4oUHMeSrc/sV"
"fileId": "dasmpuTSVM7qA3QW9/3XjQ"
},
{
"__type__": "cc.PrefabInfo",
@@ -8459,14 +8475,14 @@
"asset": {
"__id__": 0
},
"fileId": "4dcNJaDM1CLI9q3fIqHZ/U",
"fileId": "eaiasKNEFO6qvodGhL4a0b",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "LINE3",
"_name": "LINE1",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8537,7 +8553,7 @@
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "a5LMIIbHtOKK10K5do5dSQ"
"fileId": "b8C5CFg5lM4oUHMeSrc/sV"
},
{
"__type__": "cc.PrefabInfo",
@@ -8547,14 +8563,14 @@
"asset": {
"__id__": 0
},
"fileId": "a6CBSkrzZIurlX0D/HzXDp",
"fileId": "4dcNJaDM1CLI9q3fIqHZ/U",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "BOOS",
"_name": "LINE3",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
@@ -8625,7 +8641,7 @@
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "3fVB8r2UFAB5QujD6LOw6s"
"fileId": "a5LMIIbHtOKK10K5do5dSQ"
},
{
"__type__": "cc.PrefabInfo",
@@ -8635,7 +8651,7 @@
"asset": {
"__id__": 0
},
"fileId": "f4iFaXCXNKorfGJFH32POa",
"fileId": "a6CBSkrzZIurlX0D/HzXDp",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
@@ -8893,6 +8909,8 @@
"__id__": 0
},
"fileId": "35iAcUUYpE8akTtTCX1ITo",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
@@ -8932,6 +8950,8 @@
"__id__": 0
},
"fileId": "68Q5vEQWBINrggngtTZ7wh",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{

View File

@@ -7,21 +7,255 @@
"embeddedPlayerGroups": []
},
"_native": "",
"sample": 8,
"sample": 24,
"speed": 1,
"wrapMode": 2,
"wrapMode": 1,
"enableTrsBlending": false,
"_duration": 0,
"_duration": 0.125,
"_hash": 500763545,
"_tracks": [],
"_tracks": [
{
"__id__": 1
}
],
"_exoticAnimation": null,
"_events": [],
"_events": [
{
"frame": 0.08333333333333333,
"func": "",
"params": []
},
{
"frame": 0.08333333333333333,
"func": "atk",
"params": []
}
],
"_embeddedPlayers": [],
"_additiveSettings": {
"__id__": 1
"__id__": 12
},
"_auxiliaryCurveEntries": []
},
{
"__type__": "cc.animation.VectorTrack",
"_binding": {
"__type__": "cc.animation.TrackBinding",
"path": {
"__id__": 2
},
"proxy": null
},
"_channels": [
{
"__id__": 4
},
{
"__id__": 6
},
{
"__id__": 8
},
{
"__id__": 10
}
],
"_nComponents": 3
},
{
"__type__": "cc.animation.TrackPath",
"_paths": [
{
"__id__": 3
},
"scale"
]
},
{
"__type__": "cc.animation.HierarchyPath",
"path": "Node"
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 5
}
},
{
"__type__": "cc.RealCurve",
"_times": [
0,
0.08333333333333333,
0.125
],
"_values": [
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1.2999999523162842,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1.2999999523162842,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": {
"tangentMode": 0
}
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1.2999999523162842,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
}
],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 7
}
},
{
"__type__": "cc.RealCurve",
"_times": [
0,
0.08333333333333333,
0.125
],
"_values": [
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1.100000023841858,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1.100000023841858,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": {
"tangentMode": 0
}
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1.100000023841858,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
}
],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 9
}
},
{
"__type__": "cc.RealCurve",
"_times": [
0,
0.08333333333333333,
0.125
],
"_values": [
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": {
"tangentMode": 0
}
},
{
"__type__": "cc.RealKeyframeValue",
"interpolationMode": 0,
"tangentWeightMode": 0,
"value": 1,
"rightTangent": 0,
"rightTangentWeight": 1,
"leftTangent": 0,
"leftTangentWeight": 1,
"easingMethod": 0,
"__editorExtras__": null
}
],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.animation.Channel",
"_curve": {
"__id__": 11
}
},
{
"__type__": "cc.RealCurve",
"_times": [],
"_values": [],
"preExtrapolation": 1,
"postExtrapolation": 1
},
{
"__type__": "cc.AnimationClipAdditiveSettings",
"enabled": false,

View File

@@ -0,0 +1,116 @@
[
{
"__type__": "cc.AnimationClip",
"_name": "atk01",
"_objFlags": 0,
"__editorExtras__": {
"embeddedPlayerGroups": []
},
"_native": "",
"sample": 24,
"speed": 1,
"wrapMode": 1,
"enableTrsBlending": false,
"_duration": 0.20833333333333334,
"_hash": 500763545,
"_tracks": [
{
"__id__": 1
}
],
"_exoticAnimation": null,
"_events": [
{
"frame": 0.08333333333333333,
"func": "",
"params": []
},
{
"frame": 0.08333333333333333,
"func": "atk",
"params": []
}
],
"_embeddedPlayers": [],
"_additiveSettings": {
"__id__": 7
},
"_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.041666666666666664,
0.08333333333333333,
0.125,
0.16666666666666666
],
"_values": [
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
"__expectedType__": "cc.SpriteFrame"
}
]
},
{
"__type__": "cc.AnimationClipAdditiveSettings",
"enabled": false,
"refClip": null
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "2.0.3",
"importer": "animation-clip",
"imported": true,
"uuid": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
"files": [
".cconb"
],
"subMetas": {},
"userData": {
"name": "atk02"
}
}

View File

@@ -0,0 +1,116 @@
[
{
"__type__": "cc.AnimationClip",
"_name": "atk01",
"_objFlags": 0,
"__editorExtras__": {
"embeddedPlayerGroups": []
},
"_native": "",
"sample": 24,
"speed": 1,
"wrapMode": 1,
"enableTrsBlending": false,
"_duration": 0.20833333333333334,
"_hash": 500763545,
"_tracks": [
{
"__id__": 1
}
],
"_exoticAnimation": null,
"_events": [
{
"frame": 0.08333333333333333,
"func": "",
"params": []
},
{
"frame": 0.08333333333333333,
"func": "atk",
"params": []
}
],
"_embeddedPlayers": [],
"_additiveSettings": {
"__id__": 7
},
"_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.041666666666666664,
0.08333333333333333,
0.125,
0.16666666666666666
],
"_values": [
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
"__expectedType__": "cc.SpriteFrame"
}
]
},
{
"__type__": "cc.AnimationClipAdditiveSettings",
"enabled": false,
"refClip": null
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "2.0.3",
"importer": "animation-clip",
"imported": true,
"uuid": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
"files": [
".cconb"
],
"subMetas": {},
"userData": {
"name": "atk03"
}
}

View File

@@ -0,0 +1,116 @@
[
{
"__type__": "cc.AnimationClip",
"_name": "atk01",
"_objFlags": 0,
"__editorExtras__": {
"embeddedPlayerGroups": []
},
"_native": "",
"sample": 24,
"speed": 1,
"wrapMode": 1,
"enableTrsBlending": false,
"_duration": 0.20833333333333334,
"_hash": 500763545,
"_tracks": [
{
"__id__": 1
}
],
"_exoticAnimation": null,
"_events": [
{
"frame": 0.08333333333333333,
"func": "",
"params": []
},
{
"frame": 0.08333333333333333,
"func": "atk",
"params": []
}
],
"_embeddedPlayers": [],
"_additiveSettings": {
"__id__": 7
},
"_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.041666666666666664,
0.08333333333333333,
0.125,
0.16666666666666666
],
"_values": [
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
"__expectedType__": "cc.SpriteFrame"
}
]
},
{
"__type__": "cc.AnimationClipAdditiveSettings",
"enabled": false,
"refClip": null
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "2.0.3",
"importer": "animation-clip",
"imported": true,
"uuid": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
"files": [
".cconb"
],
"subMetas": {},
"userData": {
"name": "atk04"
}
}

View File

@@ -0,0 +1,116 @@
[
{
"__type__": "cc.AnimationClip",
"_name": "atk01",
"_objFlags": 0,
"__editorExtras__": {
"embeddedPlayerGroups": []
},
"_native": "",
"sample": 24,
"speed": 1,
"wrapMode": 1,
"enableTrsBlending": false,
"_duration": 0.20833333333333334,
"_hash": 500763545,
"_tracks": [
{
"__id__": 1
}
],
"_exoticAnimation": null,
"_events": [
{
"frame": 0.08333333333333333,
"func": "",
"params": []
},
{
"frame": 0.08333333333333333,
"func": "atk",
"params": []
}
],
"_embeddedPlayers": [],
"_additiveSettings": {
"__id__": 7
},
"_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.041666666666666664,
0.08333333333333333,
0.125,
0.16666666666666666
],
"_values": [
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
"__expectedType__": "cc.SpriteFrame"
},
{
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
"__expectedType__": "cc.SpriteFrame"
}
]
},
{
"__type__": "cc.AnimationClipAdditiveSettings",
"enabled": false,
"refClip": null
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "2.0.3",
"importer": "animation-clip",
"imported": true,
"uuid": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
"files": [
".cconb"
],
"subMetas": {},
"userData": {
"name": "atk05"
}
}

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -1,7 +1,7 @@
[
{
"__type__": "cc.Prefab",
"_name": "atk",
"_name": "atk1",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
@@ -13,7 +13,7 @@
},
{
"__type__": "cc.Node",
"_name": "atk",
"_name": "atk1",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
@@ -108,8 +108,8 @@
},
"_lscale": {
"__type__": "cc.Vec3",
"x": -0.5,
"y": 0.5,
"x": 1.3,
"y": 1.1,
"z": 1
},
"_mobility": 0,
@@ -136,8 +136,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 139,
"height": 128
"width": 95,
"height": 96
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -186,7 +186,7 @@
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": {
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
"__expectedType__": "cc.SpriteAtlas"
},
"_id": ""
@@ -222,8 +222,8 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 30,
"height": 50
"width": 120,
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -248,8 +248,13 @@
"__prefab": {
"__id__": 11
},
"atk_x": 0,
"atk_y": 0,
"atk_x": 20,
"atk_y": 40,
"runType": 2,
"endType": 0,
"speed": 720,
"time": 0,
"distance": 100,
"_id": ""
},
{
@@ -315,8 +320,8 @@
},
"_size": {
"__type__": "cc.Size",
"width": 30,
"height": 50
"width": 120,
"height": 120
},
"_id": ""
},
@@ -336,7 +341,7 @@
"__prefab": {
"__id__": 17
},
"playOnLoad": false,
"playOnLoad": true,
"_clips": [
{
"__uuid__": "45017156-064b-43f2-a24e-176b6ce17ad8",

View File

@@ -8,6 +8,6 @@
],
"subMetas": {},
"userData": {
"syncNodeName": "atk"
"syncNodeName": "atk1"
}
}

View File

@@ -0,0 +1,375 @@
[
{
"__type__": "cc.Prefab",
"_name": "atk2",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "atk2",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 8
},
{
"__id__": 10
},
{
"__id__": 12
},
{
"__id__": 14
},
{
"__id__": 16
}
],
"_prefab": {
"__id__": 18
},
"_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": 1.3,
"y": 1.1,
"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": 95,
"height": 96
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_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@3c745",
"__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.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 9
},
"_contentSize": {
"__type__": "cc.Size",
"width": 120,
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
},
{
"__type__": "57aabs7TE1J5obTAZczc+64",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 11
},
"atk_x": 20,
"atk_y": 40,
"runType": 2,
"endType": 0,
"speed": 720,
"time": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 13
},
"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__": 15
},
"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": 120,
"height": 120
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 17
},
"playOnLoad": true,
"_clips": [
{
"__uuid__": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
"__expectedType__": "cc.AnimationClip"
}
],
"_defaultClip": {
"__uuid__": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
"__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
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "ff6e9a75-b1b9-448e-9cbd-d30de98fd21b",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "atk2"
}
}

View File

@@ -0,0 +1,375 @@
[
{
"__type__": "cc.Prefab",
"_name": "atk3",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "atk3",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 8
},
{
"__id__": 10
},
{
"__id__": 12
},
{
"__id__": 14
},
{
"__id__": 16
}
],
"_prefab": {
"__id__": 18
},
"_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": 1.3,
"y": 1.1,
"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": 95,
"height": 96
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_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@3c745",
"__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.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 9
},
"_contentSize": {
"__type__": "cc.Size",
"width": 120,
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
},
{
"__type__": "57aabs7TE1J5obTAZczc+64",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 11
},
"atk_x": 20,
"atk_y": 40,
"runType": 2,
"endType": 0,
"speed": 720,
"time": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 13
},
"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__": 15
},
"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": 120,
"height": 120
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 17
},
"playOnLoad": true,
"_clips": [
{
"__uuid__": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
"__expectedType__": "cc.AnimationClip"
}
],
"_defaultClip": {
"__uuid__": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
"__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
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "16ca6f33-f910-4202-add4-2862be6c9127",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "atk3"
}
}

View File

@@ -0,0 +1,375 @@
[
{
"__type__": "cc.Prefab",
"_name": "atk4",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "atk4",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 8
},
{
"__id__": 10
},
{
"__id__": 12
},
{
"__id__": 14
},
{
"__id__": 16
}
],
"_prefab": {
"__id__": 18
},
"_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": 1.3,
"y": 1.1,
"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": 95,
"height": 96
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_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@3c745",
"__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.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 9
},
"_contentSize": {
"__type__": "cc.Size",
"width": 120,
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
},
{
"__type__": "57aabs7TE1J5obTAZczc+64",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 11
},
"atk_x": 20,
"atk_y": 40,
"runType": 2,
"endType": 0,
"speed": 720,
"time": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 13
},
"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__": 15
},
"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": 120,
"height": 120
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 17
},
"playOnLoad": true,
"_clips": [
{
"__uuid__": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
"__expectedType__": "cc.AnimationClip"
}
],
"_defaultClip": {
"__uuid__": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
"__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
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "7f30cd3c-57d1-476a-96bb-02c4eb082576",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "atk4"
}
}

View File

@@ -0,0 +1,375 @@
[
{
"__type__": "cc.Prefab",
"_name": "atk5",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "atk5",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 8
},
{
"__id__": 10
},
{
"__id__": 12
},
{
"__id__": 14
},
{
"__id__": 16
}
],
"_prefab": {
"__id__": 18
},
"_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": 1.3,
"y": 1.1,
"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": 95,
"height": 96
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_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@3c745",
"__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.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 9
},
"_contentSize": {
"__type__": "cc.Size",
"width": 120,
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
},
{
"__type__": "57aabs7TE1J5obTAZczc+64",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 11
},
"atk_x": 20,
"atk_y": 40,
"runType": 2,
"endType": 0,
"speed": 720,
"time": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 13
},
"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__": 15
},
"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": 120,
"height": 120
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 17
},
"playOnLoad": true,
"_clips": [
{
"__uuid__": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
"__expectedType__": "cc.AnimationClip"
}
],
"_defaultClip": {
"__uuid__": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
"__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
}
]

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "7227b42b-9cc7-41bd-8858-9d05c4cc758b",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "atk5"
}
}

View File

@@ -282,7 +282,11 @@
},
"atk_x": 10,
"atk_y": 25,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -282,7 +282,11 @@
},
"atk_x": 10,
"atk_y": 25,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -282,7 +282,11 @@
},
"atk_x": 10,
"atk_y": 25,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -253,7 +253,11 @@
},
"atk_x": 0,
"atk_y": 0,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -368,4 +372,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -389,7 +389,11 @@
},
"atk_x": 0,
"atk_y": 0,
"_id": ""
"_id": "",
"runType": 0,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -504,4 +508,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

View File

@@ -250,7 +250,11 @@
},
"atk_x": 10,
"atk_y": 15,
"_id": ""
"_id": "",
"runType": 1,
"endType": 2,
"speed": 720,
"time": 0
},
{
"__type__": "cc.CompPrefabInfo",
@@ -336,4 +340,4 @@
"instance": null,
"targetOverrides": null
}
]
]

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: 926 KiB

After

Width:  |  Height:  |  Size: 982 KiB

View File

@@ -232,7 +232,7 @@
"_top": 0,
"_bottom": 546.004,
"_horizontalCenter": 0,
"_verticalCenter": 143,
"_verticalCenter": -7,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
@@ -276,7 +276,7 @@
"value": {
"__type__": "cc.Vec3",
"x": 0,
"y": 783,
"y": 633,
"z": 0
}
},

View File

@@ -104,12 +104,12 @@ export const BuffList: Record<number, BuffConfig> = {
modifiers: [{ attr: Attrs.critical, op: ModOp.Flat, value: 30 }],
info: "暴击率提升 30%,持续 5 秒",
},
// 击退概率强化(限时)
// 击退距离强化(限时)
7013: {
id: 7013, name: "击退爆发", icon: "Stat_Stun_01",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.knockback_chance, op: ModOp.Flat, value: 30 }],
info: "击退概率提升 30%,持续 5 秒",
info: "击退距离提升 30持续 5 秒",
},
// 穿透强化(限时)
7014: {

View File

@@ -46,7 +46,6 @@ export enum CardTriggerType {
Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发
Field = 3, // 驻场光环(俗称「装备」):被动生效,由 field 数组驱动,支持单卡多词条
FightStart = 4, // 战斗开始时触发
FightEnd = 5, // 战斗结束时触发(每回合结束)
HeroDead = 6, // 场上己方英雄死亡时触发
HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活)
}
@@ -78,7 +77,7 @@ export interface CardConfig {
/** 触发类型(必填,技能卡专用;功能卡/英雄卡可缺省) */
trigger_type?: CardTriggerType;
/**
* 事件型触发的全局次数上限(仅 FightStart/FightEnd/HeroDead/HeroCall 有效)
* 事件型触发的全局次数上限(仅 FightStart/HeroDead/HeroCall 有效)
* 默认 Infinity达到上限后销毁节点
* 注意:与 t_times 语义不同——t_times 控制每回合内 Interval 的次数
*/

View File

@@ -79,7 +79,6 @@ const EquipRawList: EquipRawData[] = [
{ uuid: 8811, card_lv: 3, icon: "Wand-FA_WP_Main_Wand_004_WoodGold", name: "召唤强化", info: "召唤触发技能次数+1", field: [{ uuid: 7014, value: 1 }] },
{ uuid: 8812, card_lv: 3, icon: "Staff-FA_WP_Main_Staff_005_WoodGold", name: "死亡强化", info: "死亡触发技能次数+1", field: [{ uuid: 7015, value: 1 }] },
{ uuid: 8813, card_lv: 3, icon: "Spear-FA_WP_Main_Spear_008_GrayGold", name: "开场强化", info: "战斗开始触发技能次数+1", field: [{ uuid: 7016, value: 1 }] },
{ uuid: 8814, card_lv: 3, icon: "Blunt-FA_WP_Main_Blunt_006_OrangeGray", name: "结束强化", info: "战斗结束触发技能次数+1", field: [{ uuid: 7017, value: 1 }] },
{ uuid: 8815, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "攻击强化", info: "攻击触发技能次数+1", field: [{ uuid: 7018, value: 1 }] },
{ uuid: 8816, card_lv: 3, icon: "Shield-FA_WP_Sub_Shield_009_RedGold", name: "受击强化", info: "被攻击触发技能次数+1", field: [{ uuid: 7019, value: 1 }] },
// 多词条装备

View File

@@ -36,7 +36,6 @@ export enum GameEvent {
FightStart = "FightStart",
FightPause = "FightPause",
FightResume = "FightResume",
FightEnd = "FightEnd",
MissionEnd = "MissionEnd",
MissionComplete = "MissionComplete",//战斗结算完成
HeroSkillSelect = "HeroSkillSelect",

View File

@@ -10,10 +10,10 @@ export enum BoxSet {
MONSTER = 2,
HERO = 4,
//地图边界
LETF_END = -360,
RIGHT_END = 360,
LETF_END = -320,
RIGHT_END = 320,
//游戏地平线
GAME_LINE = 160,
GAME_LINE = 10,
/** 技能/装备各自独立的上限 */
BOX_MAX = 6,
}
@@ -37,14 +37,14 @@ export enum FightSet {
/** 英雄属性随等级成长的倍率ap/hp = base * MULTIPLIER^(lv-1) */
HERO_LV_MULTIPLIER = 2,
// BACK_RANG=30,//后退范围
BACK_RANG = 30,//后退范围
BACK_RANG = 20,//后退范围
FiIGHT_TIME = 30,//战斗时间
// BACK_CHANCE=40,//击退概率
FROST_TIME = 3,//冰冻时间
STUN_TIME = 2,//击晕时间
SKILL_CAST_DELAY = 0.15,
CSKILL_START_X = -340,
CSKILL_START_Y = 30,
/** 护盾数值上限数值减免型1 点护盾吸收 1 点伤害) */
SHIELD_MAX = 5,
WAVE_HEAL_RATE = 0.4, // 回合结束时所有英雄恢复最大生命值的比例(与 DDA 放水兜底分层:回血保节奏,放水防崩盘)
PUNCTURE_DOWN = 50,

View File

@@ -31,7 +31,7 @@ export enum Attrs {
freeze_res = "freeze_res", // 冰冻抗性
stun_chance = "stun_chance", // 击晕概率
stun_res = "stun_res", // 击晕抗性
knockback_chance = "knockback_chance", // 击退概率
knockback_chance = "knockback_chance", // 击退强化(原击退概率,现转换为击退距离增量)
knockback_distance = "knockback_distance", // 击退距离强化
knockback_res = "knockback_res", // 击退抗性
invincible_time = "invincible_time",// 无敌时间

View File

@@ -45,7 +45,6 @@ export interface ISkillDescSource {
call?: TriggerGrouped;
dead?: TriggerGrouped;
fstart?: TriggerGrouped;
fend?: TriggerGrouped;
atking?: TriggerGrouped;
atked?: TriggerGrouped;
field?: LvFieldEntry[];
@@ -85,7 +84,6 @@ const TRIGGER_KEYS: SkillTriggerType[] = [
SkillTriggerType.Call,
SkillTriggerType.Dead,
SkillTriggerType.FStart,
SkillTriggerType.FEnd,
SkillTriggerType.Atking,
SkillTriggerType.Atked,
];

View File

@@ -38,7 +38,7 @@ const ItemCardData: ItemCardRaw[] = [
// ==================== 计时性强化(统一 5s ====================
{ uuid: 9102, skill: 6503, icon: "Sub_Item-FA_WP_Sub_Potion_001_Red", name: "攻击药水", info: "全体友方攻击力提升", cost: 0, weight: 15 },
{ uuid: 9103, skill: 6504, icon: "Sub_Item-FA_WP_Sub_Potion_001_Yellow", name: "暴击药水", info: "全体友方暴击率提升", cost: 0, weight: 15 },
{ uuid: 9104, skill: 6505, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "击退药水", info: "全体友方击退概率提升", cost: 0, weight: 12 },
{ uuid: 9104, skill: 6505, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "击退药水", info: "全体友方击退距离提升", cost: 0, weight: 12 },
{ uuid: 9105, skill: 6506, icon: "Sub_Item-FA_WP_Sub_Potion_001_Blue", name: "穿透药水", info: "全体友方穿透概率提升", cost: 0, weight: 12 },
{ uuid: 9106, skill: 6507, icon: "Sub_Item-FA_WP_Sub_Potion_001_Red", name: "攻速药水", info: "全体友方攻击速度提升", cost: 0, weight: 15 },
{ uuid: 9107, skill: 6508, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "风怒药水", info: "全体友方风怒概率提升", cost: 0, weight: 12 },

View File

@@ -221,7 +221,7 @@ export const SkillDescSet: Record<number, SkillDescConfig> = {
templTimed: "{target}{verb}{value}{unit},持续{dur}秒",
},
6505: {
verb: "击退概率提升", unit: "%", permField: "ap", timedField: "buff_value",
verb: "击退距离提升", unit: "", permField: "ap", timedField: "buff_value",
templPerm: "{target}{verb}{value}{unit}",
templTimed: "{target}{verb}{value}{unit},持续{dur}秒",
},
@@ -287,7 +287,6 @@ export const FieldDescSet: Record<number, FieldDescConfig> = {
[FieldSkillType.SummonCount]: { verb: "召唤触发技能次数", fmt: FieldValueFmt.Int },
[FieldSkillType.DeadCount]: { verb: "死亡触发技能次数", fmt: FieldValueFmt.Int },
[FieldSkillType.StartCount]: { verb: "战斗开始触发技能次数", fmt: FieldValueFmt.Int },
[FieldSkillType.EndCount]: { verb: "战斗结束触发技能次数", fmt: FieldValueFmt.Int },
[FieldSkillType.WaveGold]: { verb: "每回合金币收益", fmt: FieldValueFmt.Int },
[FieldSkillType.SellGold]: { verb: "卖出英雄金币", fmt: FieldValueFmt.Int },
[FieldSkillType.WaveHeal]: { verb: "战斗结束生命回复量", fmt: FieldValueFmt.Percent },

View File

@@ -1,4 +1,4 @@
// ========== 从 HeroAttrs.ts 导入属性相关定义 ==========
// ========== 从 HeroAttrs.ts 导入属性相关定义 ==========
import { Attrs } from "./HeroAttrs";
export enum HSSet {
@@ -152,7 +152,7 @@ export interface SkillConfig {
crt?: number, // 额外暴击率
frz?: number, // 额外冰冻概率
stun?: number, // 额外击晕概率
bck?: number, // 额外击退概率
bck?: number, // 额外击退强化(击退距离增量)
buff_type?: Attrs, // Buff 类型 (单一职责)
timed_buff_id?: number, // 计时性 buff 配置 id指向 BuffList存在时走 buffManager 而非永久 add_*
buff_value?: number, // 覆写计时 buff 的修饰数值modifiers.value / tick.damage_or_heal由药水卡自定义档位
@@ -218,28 +218,28 @@ 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: "造成攻击力100%的伤害",
},
6002: {
6002: {
uuid: 6002, name: "飞刀", sp_name: "a2", icon: "Sword-FA_WP_Main_Sword_009_GraySilver", 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.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
6003: {
6003: {
uuid: 6003, name: "匕首", sp_name: "a3", icon: "Sword-FA_WP_Main_Sword_019_WoodSilver", 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.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
6004: {
6004: {
uuid: 6004, name: "飞斧", sp_name: "a4", icon: "Axe-FA_WP_Main_Axe_004_WoodGray", 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.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
6005: {
6005: {
uuid: 6005, name: "飞枪", sp_name: "a5", icon: "Spear-FA_WP_Main_Spear_010_MetalBlue", 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.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
6006: {
uuid: 6006, name: "攻击", sp_name: "atk", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
uuid: 6006, name: "攻击", sp_name: "atk1", icon: "Stat_Attack_01", 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.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
@@ -363,7 +363,7 @@ export const SkillSet: Record<number, SkillConfig> = {
6406: {
uuid: 6406, name: "击退强化", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 1, 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, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退概率限时提升, 持续5秒",
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退距离限时提升, 持续5秒",
},
6407: {
uuid: 6407, name: "距推强化", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
@@ -416,7 +416,7 @@ export const SkillSet: Record<number, SkillConfig> = {
6505: {
uuid: 6505, name: "击退爆发", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退概率提升30%, 持续5秒",
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退距离提升30, 持续5秒",
},
6506: {
uuid: 6506, name: "穿透爆发", sp_name: "buff_wind", icon: "Stat_Tripleshot", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
@@ -460,7 +460,6 @@ export enum FieldSkillType {
SummonCount = 1, // 召唤触发技能次数提升
DeadCount = 2, // 死亡触发技能次数提升
StartCount = 3, // 战斗开始触发技能次数提升
EndCount = 4, // 战斗结束触发技能次数提升
WaveGold = 5, // 每回合金币收益提升
SellGold = 6, // 卖出英雄金币提升
WaveHeal = 7, // 战斗结束生命回复量提升
@@ -515,7 +514,6 @@ export const FieldSkillSet: Record<number, FieldSkillConfig> = {
7014: { uuid: 7014, name: "召唤强化", icon: "Stat_UnitSummonIncrease_02", type: FieldSkillType.SummonCount, info: "召唤触发技能次数提升" },
7015: { uuid: 7015, name: "死亡强化", icon: "Stat_PoisonChanceIncrease", type: FieldSkillType.DeadCount, info: "死亡触发技能次数提升" },
7016: { uuid: 7016, name: "开场强化", icon: "Stat_AttackRangeIncrease_01", type: FieldSkillType.StartCount, info: "战斗开始触发技能次数提升" },
7017: { uuid: 7017, name: "结束强化", icon: "Stat_UnitSummonIncrease_01", type: FieldSkillType.EndCount, info: "战斗结束触发技能次数提升" },
7018: { uuid: 7018, name: "攻击强化", icon: "Stat_Attack_03", type: FieldSkillType.AtkCount, info: "攻击触发技能次数提升" },
7019: { uuid: 7019, name: "受击强化", icon: "Stat_Armor_01", type: FieldSkillType.BeAtkCount, info: "被攻击触发技能次数提升" },
7020: { uuid: 7020, name: "穿透强化", icon: "Stat_Tripleshot", type: FieldSkillType.HeroPunctureDmg, info: "英雄穿透后伤害提升(百分点)" },

View File

@@ -40,37 +40,22 @@ export const MonTypeName: Record<number, string> = {
/**
* 英雄攻击速度分级配置 (数值为攻击间隔cd越小越快)
* 每档3个速度级别Lv1(最快) → Lv2(中间) → Lv3(最慢)
* 6档共18级覆盖所有英雄/怪物的攻击间隔
* 5 档速度:最快 VeryFast 0.75s → 最慢 VerySlow 1.50s
*/
export enum AtkSpeedLv {
VeryFast1 = 1, VeryFast2 = 2, VeryFast3 = 3,
Fast1 = 4, Fast2 = 5, Fast3 = 6,
Normal1 = 7, Normal2 = 8, Normal3 = 9,
Mid1 = 10, Mid2 = 11, Mid3 = 12,
Slow1 = 13, Slow2 = 14, Slow3 = 15,
VerySlow1 = 16, VerySlow2 = 17, VerySlow3 = 18,
VeryFast = 1,
Fast = 2,
Normal = 3,
Slow = 4,
VerySlow = 5,
}
export const AtkSpeedSet = {
[AtkSpeedLv.VeryFast1]: { name: "极速++", cd: 0.15 },
[AtkSpeedLv.VeryFast2]: { name: "极速+", cd: 0.20 },
[AtkSpeedLv.VeryFast3]: { name: "速", cd: 0.30 },
[AtkSpeedLv.Fast1]: { name: "快速++", cd: 0.40 },
[AtkSpeedLv.Fast2]: { name: "快速+", cd: 0.50 },
[AtkSpeedLv.Fast3]: { name: "快速", cd: 0.70 },
[AtkSpeedLv.Normal1]: { name: "中速++", cd: 0.80 },
[AtkSpeedLv.Normal2]: { name: "中速+", cd: 0.90 },
[AtkSpeedLv.Normal3]: { name: "中速", cd: 1.00 },
[AtkSpeedLv.Mid1]: { name: "一般+", cd: 1.10 },
[AtkSpeedLv.Mid2]: { name: "一般", cd: 1.20 },
[AtkSpeedLv.Mid3]: { name: "一般-", cd: 1.30 },
[AtkSpeedLv.Slow1]: { name: "慢", cd: 1.50 },
[AtkSpeedLv.Slow2]: { name: "慢+", cd: 1.60 },
[AtkSpeedLv.Slow3]: { name: "慢++", cd: 1.80 },
[AtkSpeedLv.VerySlow1]: { name: "很慢", cd: 2.30 },
[AtkSpeedLv.VerySlow2]: { name: "很慢+", cd: 2.50 },
[AtkSpeedLv.VerySlow3]: { name: "很慢++", cd: 2.80 },
[AtkSpeedLv.VeryFast]: { name: "极速", cd: 0.75 },
[AtkSpeedLv.Fast]: { name: "快速", cd: 0.90 },
[AtkSpeedLv.Normal]: { name: "速", cd: 1.05 },
[AtkSpeedLv.Slow]: { name: "慢速", cd: 1.20 },
[AtkSpeedLv.VerySlow]: { name: "极慢", cd: 1.50 },
};
export const HeroPos = {
@@ -85,9 +70,9 @@ export const FormationPointX = {
[HType.Long]: 100,
} as const;
/** 各攻击定位的默认攻击距离像素近战150 / 中程300 / 远程450英雄配置了 dis 时优先取配置值 */
/** 各攻击定位的默认攻击距离像素近战100 / 中程300 / 远程450英雄配置了 dis 时优先取配置值 */
export const HeroDisVal: Record<HType.Melee | HType.Mid | HType.Long, number> = {
[HType.Melee]: 150,
[HType.Melee]: 100,
[HType.Mid]: 300,
[HType.Long]: 450,
}
@@ -179,7 +164,6 @@ export enum SkillTriggerType {
Call = "call", // 召唤后触发 降临
Dead = "dead", // 死亡后触发 遗志
FStart = "fstart", // 战斗开始 先手
FEnd = "fend", // 战斗结束 终战
Field = "field", // 驻场 光环
Atking = "atking", // 攻击后触发 追击
Atked = "atked", // 受击后触发 反击
@@ -190,7 +174,6 @@ export const SkillTriggerName = {
[SkillTriggerType.Call]: "召唤",
[SkillTriggerType.Dead]: "死亡后",
[SkillTriggerType.FStart]: "回合开始",
[SkillTriggerType.FEnd]: "回合结束",
[SkillTriggerType.Field]: "光环",
[SkillTriggerType.Atking]: "攻击",
[SkillTriggerType.Atked]: "受伤",
@@ -201,7 +184,6 @@ export const SkillTriggerDesc = {
[SkillTriggerType.Call]: "召唤时",
[SkillTriggerType.Dead]: "死亡时",
[SkillTriggerType.FStart]: "战斗开始时",
[SkillTriggerType.FEnd]: "战斗结束时",
[SkillTriggerType.Field]: "光环",
[SkillTriggerType.Atking]: "攻击n次",
[SkillTriggerType.Atked]: "受伤n次",
@@ -313,7 +295,6 @@ export interface heroInfo {
[SkillTriggerType.Call]?: TriggerGrouped; // 召唤后触发的技能配置(按 s_uuid 分组,每组按 lv 升序)
[SkillTriggerType.Dead]?: TriggerGrouped; // 死亡后触发的技能配置
[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: 触发所需的受击次数
@@ -404,7 +385,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5001 铁壁战士 — atked 护盾标杆原5011
5001: {
uuid: 5001, name: "铁壁战士", path: "hk1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 300, ap: 22,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
atked: {
6301: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 2 } },
@@ -417,7 +398,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5002 影袭刺客 — atking 自身攻击
5002: {
uuid: 5002, name: "影袭刺客", path: "hc1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 300, ap: 22,
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
atking: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 1 } },
@@ -430,7 +411,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5003 遗志将军 — dead 全队攻击
5003: {
uuid: 5003, name: "遗志将军", path: "hk2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 300, ap: 22,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
dead: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
@@ -443,7 +424,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5004 破晓先知 — fstart 全队攻击
5004: {
uuid: 5004, name: "破晓先知", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 143, ap: 32,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
fstart: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
@@ -456,7 +437,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5005 战歌法师 — field 攻击光环
5005: {
uuid: 5005, name: "战歌法师", path: "hm2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 143, ap: 32,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7002, value: 0.1 }] },
{ lv: 3, uuids: [{ uuid: 7002, value: 0.2 }] },
@@ -470,7 +451,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5101 不死斗士 — atked 回血(更高数值)
5101: {
uuid: 5101, name: "不死斗士", path: "hk3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 600, ap: 46,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
atked: {
6302: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 200 } },
@@ -483,7 +464,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5102 致命杀手 — atking 暴击(更高数值)
5102: {
uuid: 5102, name: "致命杀手", path: "hc2", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 600, ap: 46,
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
atking: {
6403: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
@@ -496,7 +477,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5103 殉道勇士 — dead 全队护盾(更高数值)
5103: {
uuid: 5103, name: "殉道勇士", path: "hk4", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 600, ap: 46,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
dead: {
6301: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
@@ -509,7 +490,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5104 圣光祭司 — fstart 全队护盾(更高数值)
5104: {
uuid: 5104, name: "圣光祭司", path: "hm3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 287, ap: 64,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow1].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
fstart: {
6301: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
@@ -522,7 +503,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5105 风怒先知 — field 攻速光环
5105: {
uuid: 5105, name: "风怒先知", path: "hm1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 287, ap: 64,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7006, value: 0.1 }] },
{ lv: 3, uuids: [{ uuid: 7006, value: 0.2 }] },
@@ -533,7 +514,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5106 破甲射手 — atking 自身穿透伤害计时buff增强
5106: {
uuid: 5106, name: "破甲射手", path: "ha1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 287, ap: 64,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
atking: {
6512: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, timed_buff_id: 7022, buff_value: 5, buff_duration: 5 } },
@@ -549,7 +530,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5201 怒血勇士 — atked 全队攻击(范围扩大)
5201: {
uuid: 5201, name: "怒血勇士", path: "hk5", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 900, ap: 68,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
atked: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 1 } },
@@ -562,7 +543,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5202 援护射手 — atking 队友攻击(范围扩大)
5202: {
uuid: 5202, name: "援护射手", path: "ha1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 430, ap: 96,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } },
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
atking: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 1, ap: 1 } },
@@ -575,7 +556,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5203 传承卫士 — dead 全队生命(范围扩大)
5203: {
uuid: 5203, name: "传承卫士", path: "hk1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 900, ap: 68,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
dead: {
6402: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 10 } },
@@ -588,7 +569,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5204 晨光贤者 — fstart 全队暴击(范围扩大)
5204: {
uuid: 5204, name: "晨光贤者", path: "hm2", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 430, ap: 96,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
fstart: {
6403: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
@@ -601,7 +582,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5205 精准术士 — field 暴击光环(范围扩大)
5205: {
uuid: 5205, name: "精准术士", path: "hm3", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 430, ap: 96,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7004, value: 0.1 }] },
{ lv: 3, uuids: [{ uuid: 7004, value: 0.2 }] },
@@ -615,7 +596,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5301 复仇守护者 — atked 全队暴击(范围+数值)
5301: {
uuid: 5301, name: "复仇守护者", path: "hk2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1200, ap: 90,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow1].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
atked: {
6403: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
@@ -628,7 +609,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5302 破灭游侠 — atking 队友暴伤(范围+数值)
5302: {
uuid: 5302, name: "破灭游侠", path: "ha2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 573, ap: 128,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal1].cd, ccd: 0 } },
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
atking: {
6404: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 2, timed_buff_id: 7020, buff_value: 5, buff_duration: 5 } },
@@ -641,7 +622,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5303 亡语刺客 — dead 全队暴击+暴伤(双效果)
5303: {
uuid: 5303, name: "亡语刺客", path: "hc3", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 1200, ap: 90,
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
dead: {
6403: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
@@ -659,7 +640,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5304 战吼术士 — fstart 全队攻击+生命(双效果)
5304: {
uuid: 5304, name: "战吼术士", path: "hm1", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 573, ap: 128,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid1].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
fstart: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
@@ -677,7 +658,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5305 领域贤者 — field 暴伤+攻速光环(双效果)
5305: {
uuid: 5305, name: "领域贤者", path: "hm2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 573, ap: 128,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
field: [
{ lv: 1, uuids: [{ uuid: 7005, value: 0.2 }, { uuid: 7006, value: 0.1 }] },
{ lv: 3, uuids: [{ uuid: 7005, value: 0.4 }, { uuid: 7006, value: 0.2 }] },
@@ -691,7 +672,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5401 不灭战魂 — atked 攻击 + revive双技能
5401: {
uuid: 5401, name: "不灭战魂", path: "hk3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1500, ap: 114,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
atked: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 2 } },
@@ -709,7 +690,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5402 战歌游侠 — atking 攻击 + field 攻速光环(双技能)
5402: {
uuid: 5402, name: "战歌游侠", path: "ha3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 717, ap: 160,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } },
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
atking: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 2 } },
@@ -727,7 +708,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5403 凤凰祭司 — dead 攻击+护盾 + revive双技能
5403: {
uuid: 5403, name: "凤凰祭司", path: "hk4", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 1500, ap: 114,
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
dead: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
@@ -750,7 +731,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5404 涅槃贤者 — fstart 护盾+攻击 + revive双技能
5404: {
uuid: 5404, name: "涅槃贤者", path: "hm3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 717, ap: 160,
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } },
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
fstart: {
6301: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
@@ -773,7 +754,7 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5405 圣言诗人 — atking 治疗 + field 生命光环(双技能)
5405: {
uuid: 5405, name: "圣言诗人", path: "hh1", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Support, role: HRole.Support, hp: 717, ap: 160,
skills: { 6015: { uuid: 6015, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } },
skills: { 6015: { uuid: 6015, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
atking: {
6302: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 200 } },
@@ -790,76 +771,77 @@ export const HeroInfo: Record<number, heroInfo> = {
},
/*
*=============怪物配置列表================
* 基础近战型(lv:1) SPEED:800 |AP:12 | HP:360 | skills[0].cd=0.65
* 重型坦克型(lv:1) SPEED:800 |AP:30 | HP:1050 | skills[0].cd=2
* 远程dps(lv:1) SPEED:800 |AP:45 | HP:240 | skills[0].cd=1.5
* 远程辅助(lv:1) SPEED:800 |AP:20 | HP:240 | skills[0].cd=1
* 精英 (lv:1) SPEED:800 |AP:20 | HP:4500 | skills[0].cd=1
*=============怪物配置列表================
* 性能基准1 级怪物与 1 级英雄card_lv=1单体性能持平 ——
* 近战怪镜像 5001hp300/ap22/cd1.20),远程怪镜像 5004hp160/ap28/cd1.20
* 重型/Boss 以更高 hp 换取更低 DPS承担"墙"与"压场"定位。
* 运行时最终属性 = 本表基础值 × RogueConfig.MON_HP_MUL / MON_AP_MUL
* Boss 额外 × BOSS_HP_BONUS_MUL校准目标整场战斗双方对砍 ≈ 30s。
*/
// 基础怪物 (全部固定点位站桩攻击HType仅决定是前排还是后排)
// 前排怪物 (站在前排,承受更多伤害) — v5: TD节奏CD多而弱爽感设计
// 前排怪物 — 近战镜像 1 级英雄基准hp300/ap22/cd1.20),变体微分定位
6001: {
uuid: 6001, name: "兽人战士", path: "m1", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 286, ap: 10, speed: 70,
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "基础前排怪"
uuid: 6001, name: "兽人战士", path: "m1", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 300, ap: 22, speed: 70,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "基础前排怪, 与1级近战英雄持平"
},
6002: {
uuid: 6002, name: "兽人精锐战士", path: "m2", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 390, ap: 14, speed: 110,
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "进阶前排怪, 更快更痛"
uuid: 6002, name: "兽人精锐战士", path: "m2", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 340, ap: 26, speed: 110,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "进阶前排怪, 更快更痛"
},
6003: {
uuid: 6003, name: "兽人重装兵", path: "m3", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Heavy, hp: 1105, ap: 20, speed: 50,
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "重型坦克怪, 高HP慢攻"
uuid: 6003, name: "兽人重装兵", path: "m3", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Heavy, hp: 800, ap: 14, speed: 50,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "重型坦克怪, 高HP低攻承伤位"
},
// 后排怪物 (站在后排,输出更高)
// 后排怪物 — 远程镜像 1 级法师基准hp160/ap28/cd1.20),变体微分定位
6004: {
uuid: 6004, name: "兽人射手", path: "m4", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Long, hp: 247, ap: 35, speed: 70,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排高DPS怪"
uuid: 6004, name: "兽人射手", path: "m4", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Long, hp: 160, ap: 28, speed: 70,
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排输出怪, 与1级远程英雄持平"
},
6005: {
uuid: 6005, name: "兽人刺客", path: "m5", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Assassin, hp: 273, ap: 38, speed: 130,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "高AP快速攻击刺客"
uuid: 6005, name: "兽人刺客", path: "m5", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Assassin, hp: 130, ap: 26, speed: 130,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } }, info: "高攻速低HP刺客"
},
// 特殊位怪物
6006: {
uuid: 6006, name: "骷髅领主", path: "m6", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.MeleeBoss, hp: 6500, ap: 20, speed: 60,
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排MiniBoss级坦克"
uuid: 6006, name: "骷髅领主", path: "m6", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.MeleeBoss, hp: 900, ap: 20, speed: 60,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "前排MiniBoss级坦克"
},
6007: {
uuid: 6007, name: "兽人术士", path: "m7", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Support, hp: 390, ap: 24, speed: 70,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排法师怪, 魔法攻击"
uuid: 6007, name: "兽人术士", path: "m7", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Support, hp: 240, ap: 18, speed: 70,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排辅助法师怪"
},
6008: {
uuid: 6008, name: "兽人火法", path: "m8", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Summoner, hp: 351, ap: 32, speed: 70,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "后排高输出法师怪"
uuid: 6008, name: "兽人火法", path: "m8", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Summoner, hp: 220, ap: 24, speed: 70,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排召唤系法师怪"
},
// BOSS怪物 — Boss节奏1.2-1.5s删除不存在的6206技能
// BOSS怪物 — 本表 hp/ap 仅作外观/技能档位参考,运行时面板由 RogueConfig 的
// BOSS_BASE_HP / BOSS_BASE_AP初始英雄 2 倍强度)统一决定,不读取本表数值
6101: {
uuid: 6101, name: "兽人首领-双刀战士", path: "mb1", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 2470, ap: 30, speed: 120,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排Boss, 攻速"
uuid: 6101, name: "兽人首领-双刀战士", path: "mb1", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1200, ap: 24, speed: 120,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "前排Boss, 攻速"
},
6102: {
uuid: 6102, name: "兽人首领-斧头战士", path: "mb2", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 9750, ap: 26, speed: 60,
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "前排Boss, 超高HP"
uuid: 6102, name: "兽人首领-斧头战士", path: "mb2", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1500, ap: 18, speed: 60,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "前排Boss, 超高HP"
},
6103: {
uuid: 6103, name: "兽人首领-魔法师", path: "mb3", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 2925, ap: 38, speed: 110,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "后排法系Boss, 高AP"
uuid: 6103, name: "兽人首领-魔法师", path: "mb3", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 1000, ap: 28, speed: 110,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排法系Boss"
},
6104: {
uuid: 6104, name: "兽人首领-射手", path: "mb4", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 8840, ap: 30, speed: 70,
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "后排位Boss, 均衡型"
uuid: 6104, name: "兽人首领-射手", path: "mb4", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 1100, ap: 24, speed: 70,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排位Boss, 均衡型"
},
6105: {
uuid: 6105, name: "亡灵首领-法师", path: "mb5", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 3380, ap: 42, speed: 110,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排高AP Boss"
uuid: 6105, name: "亡灵首领-法师", path: "mb5", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 1000, ap: 30, speed: 110,
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排高AP Boss"
},
6106: {
uuid: 6106, name: "亡灵首领-骑马战士", path: "mb6", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 11700, ap: 26, speed: 130,
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排终极Boss, 最高HP+高速"
uuid: 6106, name: "亡灵首领-骑马战士", path: "mb6", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1800, ap: 20, speed: 130,
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "前排终极Boss, 最高HP+高速"
},
@@ -952,7 +934,7 @@ export const HeroComboList: number[] = [8001, 8002, 8003, 8004, 8005, 8006];
* - W_HP: 每 1 点生命值折算 1 分(生命值不随时间变化)
* - W_DPS_PER_SEC: 每 1 点 DPS 持续 1 秒折算 0.5 分
* (默认 60 秒评估窗下1 DPS ≈ 30 分,与旧版 W_DPS=30 等效)
* - WAVE_DURATION / HITS_PER_WAVE: 回合参数,用于估算 atked/fstart/fend/dead 触发次数
* - WAVE_DURATION / HITS_PER_WAVE: 回合参数,用于估算 atked/fstart/dead 触发次数
* - DEFAULT_EVAL_SECONDS: calcHeroPower 默认使用的评估时间窗
*/
export const HERO_POWER_WEIGHTS = {
@@ -961,7 +943,6 @@ export const HERO_POWER_WEIGHTS = {
W_atking: 1.0,
W_atked: 0.8,
W_fstart: 1.0,
W_fend: 1.2,
W_dead: 0.6,
W_field_alive: 1.0,
W_revive: 1.0,
@@ -1085,7 +1066,6 @@ function getTriggerCount(
case SkillTriggerType.Atked:
return getHitCount(seconds) / t_num;
case SkillTriggerType.FStart:
case SkillTriggerType.FEnd:
return getWaveCount(seconds);
case SkillTriggerType.Dead:
return getWaveCount(seconds) * (1 + (resolveReviveByLv(hero.revive, FightSet.HERO_MAX_LV)?.r_num || 0));
@@ -1161,7 +1141,9 @@ function calcSupportEffectValue(config: SkillConfig, apEff: number, cd: number,
return (value / 100) * (apEff / cd) * 0.05 * weights.W_DPS_PER_SEC * seconds;
case Attrs.stun_chance:
case Attrs.freeze_chance:
return value * weights.W_control;
case Attrs.knockback_chance:
// 击退强化已改为击退距离增量,按距离价值折算
return value * weights.W_control;
case Attrs.speed:
return (value / 100) * (apEff / cd) * weights.W_DPS_PER_SEC * seconds;
@@ -1183,7 +1165,8 @@ function calcSkillEffectValue(skill: SkillConfig, overrides: SkillOverrides | un
case SkillKind.Heal:
return ((config.ap || 0) / 100) * apEff * weights.W_heal;
case SkillKind.Shield:
return (config.ap || 0) * weights.expectedEnemyDmg * weights.W_shield;
// 护盾为数值减免型1 点护盾 ≈ 1 点预期伤害减免
return (config.ap || 0) * weights.W_shield;
case SkillKind.Support:
return calcSupportEffectValue(config, apEff, cd, seconds);
default:
@@ -1214,7 +1197,6 @@ export interface HeroPowerBreakdown {
atking: number;
atked: number;
fstart: number;
fend: number;
dead: number;
field: number;
revive: number;
@@ -1229,7 +1211,7 @@ export interface HeroPowerDetailed {
normalAttack: { s_uuid: number, name: string, cd: number, dps: number, value: number };
}
/** 计算普通触发类技能atking/atked/fstart/fend/dead的详细价值 */
/** 计算普通触发类技能atking/atked/fstart/dead的详细价值 */
function calcTriggerPowerDetailed(
triggerConfigs: { s_uuid: number, t_num?: number, overrides?: SkillOverrides }[],
hero: heroInfo,
@@ -1263,9 +1245,6 @@ function calcTriggerPowerDetailed(
case SkillTriggerType.FStart:
weight = weights.W_fstart;
break;
case SkillTriggerType.FEnd:
weight = weights.W_fend;
break;
case SkillTriggerType.Dead:
weight = weights.W_dead;
break;
@@ -1362,7 +1341,6 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
const atking = hero.atking ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atking, targetLv), hero, targetLv, SkillTriggerType.Atking, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
const atked = hero.atked ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atked, targetLv), hero, targetLv, SkillTriggerType.Atked, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
const fstart = hero.fstart ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fstart, targetLv), hero, targetLv, SkillTriggerType.FStart, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
const fend = hero.fend ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fend, targetLv), hero, targetLv, SkillTriggerType.FEnd, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
const dead = hero.dead ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.dead, targetLv), hero, targetLv, SkillTriggerType.Dead, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
const fieldEntries = resolveFieldByLv(hero.field, targetLv);
const field = fieldEntries.length > 0 ? calcFieldPowerDetailed(fieldEntries, hero, targetLv, seconds) : { total: 0, items: [] as FieldSkillEval[] };
@@ -1373,7 +1351,7 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
reviveValue = hpEff * revive.upr * revive.r_num * weights.W_revive * getWaveCount(seconds);
}
const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + fend.total + dead.total + field.total + reviveValue;
const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + dead.total + field.total + reviveValue;
return {
total,
@@ -1383,12 +1361,11 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
atking: atking.total,
atked: atked.total,
fstart: fstart.total,
fend: fend.total,
dead: dead.total,
field: field.total,
revive: reviveValue,
},
triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...fend.items, ...dead.items],
triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...dead.items],
fieldSkills: field.items,
normalAttack,
};

View File

@@ -0,0 +1,361 @@
# 几何王国技能复刻设计文档
> **目的**:基于《几何王国》热门英雄技能,分析哪些可以**零代码改动**或**最小改动**复刻到本系统1 回合定生死机制)。
>
> **前置约束**(已确认的设计原则):
> - 局内固定 lv=1等级成长移交局外
> - 单场战斗 30 秒
> - card_lv 纯技能数量差异1~5 个技能槽)
> - 技能触发极简(`t_num=1`),高频高数值
> - 无相同能力英雄,无阵容限制
---
## 一、本系统可复用的"触发骨架"
| 触发类型 | 枚举 | 语义 | 几何王国对应场景 |
|---|---|---|---|
| 战斗开始 | `SkillTriggerType.FStart` | 入场时触发 1 次 | 战吼、入场护盾 |
| 战斗结束 | `SkillTriggerType.FEnd` | 战斗胜利后触发 | 30s 短局意义弱) |
| 召唤入场 | `SkillTriggerType.Call` | 落地触发 1 次 | 登场技 |
| 死亡触发 | `SkillTriggerType.Dead` | 死亡时触发 1 次 | 亡语、遗志 |
| 攻击触发 | `SkillTriggerType.Atking` | 普攻 N 次触发 | 概率普攻特效t_num 模拟概率) |
| 受击触发 | `SkillTriggerType.Atked` | 被击 N 次触发 | 受击反击、叠甲 |
| 驻场光环 | `SkillTriggerType.Field` | 存活期间全局生效 | 光环类(移速/攻击/回血) |
| 复活 | `SkillTriggerType.Revive` | 死亡后复活 | 凤凰涅槃、尸骸存活 |
> **概率模拟技巧**:本系统无"概率触发"概念,用 `t_num` 计数模拟。
> - `t_num=1`:每次普攻都触发(最高频)
> - `t_num=3`≈33% 概率
> - `t_num=4`≈25% 概率(对应几何王国的"30% 概率"
---
## 二、本系统可复用的"效果骨架"
### 2.1 SkillKind技能主效果
| 类型 | 字段 | 几何王国对应 |
|---|---|---|
| `SkillKind.Damage` | `ap` 百分比伤害 | 大招、战技伤害 |
| `SkillKind.Heal` | `ap` 治疗量 | 治疗、吸血 |
| `SkillKind.Shield` | `ap` 护盾层数(数值减免) | 暗光之盾、守护壁垒 |
| `SkillKind.Support` | `buff_type` + `timed_buff_id` | 属性强化、限时增益 |
| `SkillKind.Gold` | `gold` 金币数 | (本局不适用) |
### 2.2 击杀特效字段
| 字段 | 含义 | 几何王国对应 |
|---|---|---|
| `crt` | 额外暴击率 | 致命一击 |
| `frz` | 额外冰冻概率 | 寒冰攻击 |
| `stun` | 额外击晕概率 | 眩晕 |
| `bck` | 击退距离增量 | 击退(雷霆一击、剑刃风暴) |
| `hit_count` | 命中目标数 | 多段攻击(乱棍、剑刃风暴) |
| `puncture_chance` | 穿透概率 | 穿透(暗器大师) |
### 2.3 BuffList 已有 DoT / HoT / 控制
| Buff ID | 名称 | 类型 | 几何王国对应 |
|---|---|---|---|
| 7002 | 灼烧 | Debuff DoT-10 伤害/0.5s,吃 30% 攻击加成) | 剧毒利刃、火系持续伤害 |
| 7003 | 冰冻 | Control2s | 冰冻 |
| 7004 | 击晕 | Control2s | 眩晕、沉默(替代) |
| 7010 | 再生 | Buff HoT+10 治疗/0.5s | 持续回血 |
| 7011 | 攻击爆发 | Buff+50% 攻击 5s | 大圣归来、剑圣 1 觉 |
| 7012 | 暴击爆发 | Buff+30% 暴击 5s | 暴击强化 |
| 7017 | 击晕爆发 | Buff+30% 击晕 5s | 群体眩晕强化 |
| 7111 | 战前强攻 | Buff+30 攻击 5s | 开场增益 |
### 2.4 FieldSkillType 已有光环
| 类型 | 几何王国对应 |
|---|---|
| `HeroAtk` | 攻击光环(人鱼公主、邪恶光环) |
| `HeroCrit` / `HeroCritDamage` | 暴击光环 |
| `HeroSpeed` | 攻速光环 |
| `HeroHp` | 生命光环 |
| `HeroStun` / `HeroWindFury` / `HeroPuncture` | 特殊光环 |
| `WaveHeal` | 战后回血30s 短局可改为局内 HoT |
---
## 三、可直接复刻(零代码改动)
> 仅通过 `heroSet.ts` 配置 + 复用现有 SkillSet / BuffList 即可实现。
### 3.1 剧毒女王Mage复刻"深渊女王·剧毒利刃"
**核心机制**:普攻触发灼烧 DoT叠加毒伤害。
```typescript
[SkillTriggerType.Atking]: {
// 每次普攻上灼烧 DoT已存在 BuffList 7002
6101: [{ lv: 1, t_num: 1, overrides: { timed_buff_id: 7002, buff_duration: 5 } }],
},
```
**复刻自**:深渊女王·剧毒利刃(普攻 30% 概率 200% 法伤 + 减益)。
**适配调整**30s 战斗节奏快,`t_num=1` 100% 触发,但单层 DoT 数值调低以平衡。
---
### 3.2 猴王·乱棍Assassin复刻"至尊宝·大圣归来"
**核心机制**:普攻触发多段伤害+击晕。
```typescript
[SkillTriggerType.Atking]: {
// 每次普攻触发 4 段 30% 伤害 + 高击晕
6006: [{ lv: 1, t_num: 1, overrides: { ap: 30, hit_count: 4, stun: 30 } }],
},
```
**复刻自**:至尊宝·大圣归来(乱棍 4 次 ×30% 物伤 + 眩晕)。
**适配调整**:去除"召唤虚影+攻击+25%"(与本系统能量体系冲突),保留多段+击晕核心。
---
### 3.3 剑圣·疾风Warrior复刻"剑圣·剑刃风暴+吸血面罩"
**核心机制**:高伤范围 + 击退 + 普攻吸血。
```typescript
[SkillTriggerType.Atking]: {
// 普攻触发 360% 范围伤害 + 击退
6201: [{ lv: 1, t_num: 1, overrides: { ap: 150, bck: 30 } }],
// 普攻吸血 35%
6302: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Self, ap: 35 } }],
},
```
**复刻自**剑圣·剑刃风暴360% 物伤 + 击退) + 吸血面罩(普攻 35% 吸血)。
**适配调整**`t_num=1` 高频触发,单场 30s 内吸血量需调平衡。
---
### 3.4 壁垒贤者Support复刻"哀木涕·守护壁垒"
**核心机制**:开场全队护盾 + 驻场生命光环。
```typescript
[SkillTriggerType.FStart]: {
// 开场全队护盾(数值减免型)
6301: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } }],
},
[SkillTriggerType.Field]: [
// 驻场生命光环
{ lv: 1, s_uuid: 7007, value: 15 }, // +15% 最大生命
],
```
**复刻自**:哀木涕·守护壁垒(开场 10% 最大生命护盾 + 防御光环)。
**适配调整**:本系统护盾是"数值减免"非"层数"ap 字段意义不同,需重新校准数值。
---
### 3.5 凤凰祭司Warrior复刻"死亡守护 + 复活"
**核心机制**:死亡时为最低血友军回血 + 自身可复活。
```typescript
[SkillTriggerType.Dead]: {
// 死亡守护:死亡时为最低血友军回血
6302: [{ lv: 1, t_num: 1, overrides: { ap: 50 } }],
},
[SkillTriggerType.Revive]: [
// 复活:死亡后 1 次 50% 血复活
{ lv: 1, s_uuid: 6501, r_num: 1, upr: 50 },
],
```
**复刻自**通用觉醒·死亡守护10% 最大生命回血) + 黑暗骑士 3 觉(尸骸存活)。
**适配调整**30s 战斗中复活价值高,`r_num=1` 限定 1 次。
---
### 3.6 暗影骑士Tank复刻"黑暗骑士·死亡缠绕"
**核心机制**:普攻治疗血少友军 + 受击反击自盾。
```typescript
[SkillTriggerType.Atking]: {
// 普攻触发治疗(自动选血量最低友方,引擎已支持)
6302: [{ lv: 1, t_num: 1, overrides: { ap: 220 } }],
},
[SkillTriggerType.Atked]: {
// 受击触发自身护盾
6301: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Self, ap: 3 } }],
},
```
**复刻自**:黑暗骑士·死亡缠绕(普攻 25% 概率 220% 真伤/治疗友方)。
**适配调整**:本系统暂无"真伤",用高治疗量替代。
---
### 3.7 风暴歌者Mage复刻"深渊女王·超声波"
**核心机制**:范围伤害 + 群体击退。
```typescript
[SkillTriggerType.Atking]: {
// 范围伤害 + 群体击退
6201: [{ lv: 1, t_num: 1, overrides: { ap: 200, bck: 50, hit_count: 6 } }],
},
```
**复刻自**:深渊女王·超声波(伤害 + 击退控制)。
**适配调整**`bck` 字段直接对应击退距离增量,无需新增机制。
---
### 3.8 战吼先锋Warrior复刻"光明骑士·光明守护"
**核心机制**:开场全队增益 + 持续回血。
```typescript
[SkillTriggerType.FStart]: {
// 开场全队攻击强化
6401: [{ lv: 1, t_num: 1, overrides: { ap: 30 } }],
// 开场全队持续回血
6502: [{ lv: 1, t_num: 1, overrides: { timed_buff_id: 7010, buff_duration: 10 } }],
},
```
**复刻自**:光明骑士·光明守护(清异常 + 加防 + 持续治疗)。
**适配调整**:本系统无"解控"概念30s 战斗也不需要,改为纯增益 + HoT。
---
## 四、需要小幅扩展即可复刻
### 4.1 反伤罗刹Tank复刻"剑豪·罗生门"
**缺失机制**:本系统无"反伤"属性。
**扩展方案**
1. `HeroAttrs.ts``Attrs` 枚举新增 `thorns = "thorns"`(反伤数值)
2. `HeroAttrsComp.ts` 新增字段 `thorns: number = 0`
3. 战斗结算时,受击方对攻击者造成 `thorns` 点固定伤害
4. `SkillConfig` 新增 `thorns?: number` 用于 Support 技能附加反伤 buff
**复刻配置**
```typescript
[SkillTriggerType.Atked]: {
// 受击触发护盾 + 反伤 buff5s 内反伤 30 点)
6301: [{ lv: 1, t_num: 1, overrides: { ap: 5 } }],
6401: [{ lv: 1, t_num: 1, overrides: { buff_type: Attrs.thorns, timed_buff_id: 7030 } }],
},
```
---
### 4.2 真伤刺客Assassin复刻"玉面狐妖·真伤"
**缺失机制**:本系统伤害公式只支持 `ap × 系数`,无"按目标最大生命百分比伤害"。
**扩展方案**
1. `SkillConfig` 新增 `target_hp_pct?: number`(按目标最大生命百分比扣血)
2. 伤害结算时,若技能含此字段,额外造成 `target.hp_max × target_hp_pct / 100` 伤害
**复刻配置**
```typescript
[SkillTriggerType.Atking]: {
// 普攻附带 3% 目标最大生命真伤
6001: [{ lv: 1, t_num: 1, overrides: { ap: 100, /* 需新增 target_hp_pct: 3 */ } }],
},
```
---
### 4.3 重伤诅咒Mage复刻"巫妖王·重伤"
**缺失机制**:本系统无"降低治疗效果"属性。
**扩展方案**
1. `Attrs` 新增 `heal_reduce = "heal_reduce"`(百分比降低受治疗)
2. 治疗结算时,实际治疗量 = 原治疗量 × (1 - heal_reduce/100)
**复刻配置**
```typescript
[SkillTriggerType.Atking]: {
// 普攻附带重伤 debuff敌方受治疗 -50%
6101: [{ lv: 1, t_num: 1, overrides: { /* 需新增 heal_reduce 机制 */ } }],
},
```
---
## 五、不建议复刻
| 类型 | 代表技能 | 原因 |
|---|---|---|
| **能量/大招系统** | 所有英雄的"大招" | 本系统无能量条,技能全靠触发器,无法承载"释放大招"概念 |
| **解控** | 光明洗礼、暗黑盾解控 | 30s 战斗节奏快,控制时间仅 2~3s解控价值极低 |
| **多段位移/分身** | 炼狱鬼斩、大圣虚影 | 视觉表现层复杂ECS 弹道模型难承载"幻影穿梭" |
| **沉默 / 减能** | 潮壮壮、海拉 | 本系统无主动技能,沉默无目标可作用 |
| **长期叠层** | 剑圣 1 觉30% 防御转攻击,永久) | 30s 战斗内永久叠层易失衡 |
| **按当前生命%伤害** | 女王尖啸18% 当前生命) | 与"按最大生命%"相比30s 短局内波动过大,难平衡 |
| **击退抗性 / 减速抗性** | 剑豪红武、酒仙 | 本系统已有 `knockback_res`,但减速概念不存在 |
---
## 六、复刻优先级矩阵
| 优先级 | 英雄 | 复刻难度 | 设计价值 |
|---|---|---|---|
| **P0必做** | 剧毒女王 | ✅ 零代码 | 引入 DoT 体系,丰富伤害维度 |
| **P0** | 猴王·乱棍 | ✅ 零代码 | 多段伤害+击晕,近战核心 |
| **P0** | 剑圣·疾风 | ✅ 零代码 | 范围+击退+吸血,全能战士 |
| **P1推荐** | 壁垒贤者 | ✅ 零代码 | 团队防御核心 |
| **P1** | 凤凰祭司 | ✅ 零代码 | 复活机制已有,强化死亡触发 |
| **P1** | 暗影骑士 | ✅ 零代码 | 治疗型坦克,差异化定位 |
| **P2可选** | 风暴歌者 | ✅ 零代码 | 范围控制,与剧毒女王互补 |
| **P2** | 战吼先锋 | ✅ 零代码 | 开场增益,节奏型辅助 |
| **P3扩展后做** | 反伤罗刹 | ⚙️ 需新增 thorns | 反伤 META针对高攻速敌人 |
| **P3** | 真伤刺客 | ⚙️ 需新增 target_hp_pct | 针对高血量 Boss |
| **P3** | 重伤诅咒 | ⚙️ 需新增 heal_reduce | 针对治疗队 |
---
## 七、新增机制工作量评估
| 机制 | 改动文件 | 改动量 | 风险 |
|---|---|---|---|
| **反伤 thorns** | HeroAttrs.ts + HeroAttrsComp.ts + 战斗结算 | ~50 行 | 低(独立属性) |
| **按最大生命%伤害** | SkillSet.ts + 伤害结算 | ~30 行 | 低(额外乘区) |
| **减疗 heal_reduce** | HeroAttrs.ts + HeroAttrsComp.ts + 治疗结算 | ~40 行 | 低(独立乘区) |
| **敌方 Support 技能** | SCastSystem.ts | ~20 行 | 中(需测试目标选择) |
---
## 八、与原"20 回合制"的兼容性
本设计文档基于 **1 回合定生死** 机制,所有数值均按 **30 秒战斗窗口** 校准:
- `t_num=1` 触发30s 内普攻 30~50 次,技能触发 30~50 次
- DoT/HoT 持续 5s覆盖 1/6 战斗时长,效果显著但不至于"挂上就赢"
- 复活 `r_num=1`:单场战斗最多 1 次,价值极高但可控
- 驻场光环 `value=15%`30s 内持续生效,等价于"永久"(单场)
若未来回归多回合制,需重新校准:
- `t_num` 提高到 2~4降低触发频率
- DoT/HoT 持续时间延长至 10~15s
- 复活次数可放开到 2~3 次
---
## 九、下一步行动
1. **确认 P0 英雄清单**:是否按本方案复刻 8 个英雄?
2. **数值校准**:是否需要先用 `calcHeroPower` 公式推导每个英雄的目标强度锚点?
3. **新增机制决策**:是否在第 1 阶段引入反伤/真伤/减疗?还是先纯配置复刻?
4. **编写 heroSet.ts 配置**:确认后我可以直接输出可粘贴的 TypeScript 配置代码。
---
**文档版本**v1.0
**创建日期**2026-08-14
**作者**AI Assistant
**关联文档**[heroSet.ts](./heroSet.ts) / [SkillSet.ts](./SkillSet.ts) / [BuffSet.ts](./BuffSet.ts) / [几何王国英雄技能汇总.md](./几何王国英雄技能汇总.md)

View File

@@ -0,0 +1,11 @@
{
"ver": "1.0.1",
"importer": "text",
"imported": true,
"uuid": "4b91da8c-0510-4f36-a3f7-b78ec260c1f9",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,199 @@
# 《几何王国》(微信小游戏)英雄技能汇总
> **资料说明**:本作暂无官方公开的完整技能数据库,以下内容整理自玩家攻略站、公众号「英雄解析」系列、社区讨论等公开资料(来源见文末)。技能数值可能随版本更新调整,请以游戏内实际为准。游戏现有 45+ 英雄,本文收录主流热门英雄。
---
## 一、技能体系说明
每个英雄的技能按养成线逐步解锁 [^7^][^27^]
| 解锁节点 | 技能类型 |
|---|---|
| 初始 | 大招(主动技能) |
| 2 星 | 战技技能 |
| 5 星 | 强化技能 |
| 8 星 | 特技技能 |
| 12 星 | 属性技能 |
| 16 星 | 强化技能 |
| 觉醒 | 一觉 ~ 四觉(部分为通用技能池) |
| 神器 | 紫 / 黄 / 红 / 彩 四档神器加成 |
---
## 二、热门英雄详细技能
### 1. 黑暗骑士(神话 · 前排主坦,版本必备)
- **大招「暗光之盾」**:释放无视控制;获得 8% 最大生命值的护盾并转化为生命(持续 7 秒),其中 5% 作用于队友;提供 20% 减速抗性 [^18^]
- **缠绕(死亡缠绕)**:普攻有 25% 几率对全场血量最低的目标施放——敌方目标受到 220%+10% 神器增强)真实伤害,友方目标回复等量生命 [^18^][^75^]
- **暗黑盾**:解除控制,且附带击退效果 [^18^]
- **神器「霜之暗斧」**:叠加 3 层可沉默敌方前排并减速 [^18^]
- **彩色神器「邪恶光环」**+8% 移动速度光环 + 少量回复 [^18^]
- **觉醒**1 觉全队 +10% 魔抗3 觉阵亡后以尸体状态存活 3.5 秒,为后排争取输出时间 [^18^]
- **定位评价**:目前版本最全面的前排,治疗、推进、解控兼具,基本是必备品 [^18^]
### 2. 光明骑士(神话 · 前排辅助)
- **大招「光明守护」**:释放无视控制,清除全队异常状态;全队 +30% 防御、+10% 能量回复速度;每秒 35% 攻击力的治疗,持续 7 秒 [^18^]
- **圣洁之锤**120% 真实伤害,伤害的 70% 转化为自身治疗 [^18^]
- **光明洗礼**:每 6 秒触发一次,为 2 名队友解除异常状态(可解沉默、控制、减攻等)[^18^]
- **彩色神器「圣域守护」**:减少敌方 20% 攻速、10% 移速;每 10 秒给队友单体释放光明守护 [^18^]
### 3. 剑圣(神话 · 前排输出)
- **大招「剑刃风暴」**360% 物理伤害,击退所有角色,附带 6 秒 20% 重伤 [^68^]
- **战技「致命一击」**:普攻 35% 几率造成 1.5~2.2 倍真实伤害 [^68^]
- **分身斩**:每隔一段时间造成 5 次伤害 [^68^]
- **觉醒**1 觉将 30% 防御转换为攻击3 觉普攻随机降低对方攻击 [^68^]
- **评价**:免疫技能、群体击退,能聚集敌方后排利于己方 AOE但伤害多为打前排的"无效输出",定位偏功能性抗伤 + 击退 [^68^]
### 4. 酒仙(神话 · 为推进而生)
- **风暴战士**:清除负面效果,击退抗性 +100%,伤害减免 30%无法减免真伤75% 免控 [^68^]
- **雷霆一击**:普攻 30% 概率使范围内敌人全体减速并强力击退 [^68^]
- **酒雾火焰**:每隔一段时间造成伤害并附加 5 秒 50% 重伤效果 [^68^]
- **觉醒**1 觉普攻随机降低目标 6% 攻击或 10% 防御3 觉血量低于 50% 时5 秒免疫魔法或物理伤害 [^68^]
### 5. 剑豪(前排战士 · 针对射手/物理队)
作用:输出 + 沉默 + 减攻 + 减退抗 + 反伤BUFF/DEBUFF 全面,进可攻退可守,缺点是缺少硬控 [^7^]
- **大招「炼狱鬼斩」**:制造幻影在战场穿梭发动 6 次斩击,每次 360% 攻击的物理伤害并附带击退,每个目标至多承受 3 次,优先斩击物理伤害单位 [^7^]
- **2 星战技「三十六烦恼凤」**:每 10 秒释放剑气攻击敌方能量最高单位500% 物理伤害 + 沉默 1.5 秒(可抢第一波大招先手)[^7^]
- **5 星强化「强化斩击」**:大招命中攻击高于自己的目标时,降低其 15% 攻击 6 秒 [^7^]
- **8 星特技「罗生门」**:每累计损失 25% 最大生命,生成剑门屏障,抵消 18% 伤害 + 25% 反伤,持续 4 秒,无冷却(核心技能)[^7^]
- **12 星「武装色霸气」**:攻击 +10%、生命 +15% [^7^]
- **16 星「修罗之道」**:大招每次斩击额外造成 100% 真实伤害,并降目标 15% 击退抗性 5 秒 [^7^]
- **神器**:紫武(战技沉默结束后降目标 30% 回能 5 秒);黄武(罗生门反伤增至 55%、持续增至 6 秒);**红武「三千世界」**(入场 13 秒后目标承受伤害 +27%,自身受沉默时间 -60%);彩武(罗生门释放时立即回复 11% 最大生命 + 回能/回血加成)[^7^]
- **觉醒「空手接白刃」**:生命低于 55% 时,永久降低敌方全体 15% 物理伤害直至阵亡 [^7^]
- **培养建议**:神器最低门槛红色,推荐彩色,优先级高 [^7^]
### 6. 至尊宝(前排战士 · 功能性工具人)
作用:击退 + 眩晕 + 减防 + 受伤回血 + 扛伤;能抗能解控,劣势局有反打之力,但输出低 [^27^]
- **大招「大圣归来」**:召唤大圣虚影,期间自身攻击 +25%;乱棍打击 4 次 ×30% 物理伤害并击退,收尾一击 160% 物理伤害 + 2 秒眩晕 [^27^]
- **2 星战技「七伤拳」**:普攻 30% 概率降低目标 30% 防御,但 20% 概率降低自身 10% 防御 [^27^]
- **5 星强化「大圣之力」**:放大招时自身 +15% 击退效果 3 秒 [^27^]
- **8 星特技「般若波罗蜜」**:每 12 秒引导时空之力3 秒内受到伤害的 60% 转化为自身生命(不受重伤影响,转化总量不超过 12% 最大生命)[^27^]
- **12 星「五岳之尊」**:防御 +10%、生命 +15%**16 星「大圣之御」**:放大招时受伤 -30% 持续 6 秒 [^27^]
- **神器**:紫武(七伤拳触发概率 +10%);黄武(般若波罗蜜可作用于所有近战友军)[^27^]
### 7. 齐天大圣(神话 · T0 全能战士,人气主 C
- **主动技能「定海神针」**:对敌方前排造成范围眩晕效果,打乱敌方阵型 [^35^]
- 技能与普攻均可造成高额暴击伤害,后期伤害依然可观 [^35^][^33^]
- 强度榜长期位居 T0获取与培养成本低新手友好 [^33^][^37^]
### 8. 复仇者(神话 · T0 后排杀手)
- **「暗影突袭」**:可穿透威胁敌方后排关键目标 [^2^]
- 攻击敌人附加流血效果;获取成本较低,性价比极高 [^33^][^1^]
- 阵容中可搭配火枪,用斩击 + 狙击双重伤害秒杀敌方后排 [^14^]
### 9. 深渊女王 / 深渊女巫(法师 · 群体输出 + 控制)
- **大招「超声波」**:主力伤害 + 击退控制技能 [^60^]
- **2 星战技「剧毒利刃」**:普攻 30% 几率射出淬毒匕首200% 魔法伤害3 秒内降低目标 20% 击退抗性 [^28^]
- **5 星强化「强化声波」**:超声波击退效果 +30% [^28^]
- **8 星特技「女王尖啸」**:战斗第 20 秒发出尖啸,使所有敌方目标失去 18% 当前生命并眩晕 2 秒(每场仅 1 次,上限 350% 攻击)[^28^]
- **12 星「攻击增强」**:攻击 +25%**16 星「超级声波」**:超声波魔法伤害 +65%,被击中目标 5 秒内伤害结果 -25% [^28^]
### 10. 巫妖王(法师 · 当前版本核心)
- **核心能力是红武提供的"重伤"**——这种能力全英雄中稀缺,在爷爷、光明骑士遍地走的环境下接近必需品 [^3^]
- 彩神具有**多段控制**(连环霜冻,黑暗骑士与光明骑士都解控不过来)[^3^]
- 自带**减速和冰冻能力**,利于推进甚至壁咚,集控制、输出、重伤、推力于一体 [^3^]
- 常见搭配:巫妖王 + 黑暗骑士 + 光明骑士 + 战士(复仇者/哪吒/剑圣/船长)+ 暗器大师或影魔 [^3^]
### 11. 玉面狐妖(法师 · 真伤输出)
- 前排杀手队核心之一:真实伤害无视防御,配合爷爷的攻击与回能增益可快速消灭敌方前排 [^14^]
- 后期值得培养的法师主 C 之一 [^1^]
---
## 三、英雄觉醒技能表
以下为各英雄觉醒技能(一觉~四觉,含通用技能池)[^4^]
| 英雄 | 觉醒技能 |
|---|---|
| **斧小王** | 坚硬皮肤(受伤 +1.5% 防御,最多 12 层);梦魇肩甲;死亡守护;嗜血战斧(每次释放「淘汰之斧」回复 15% 已损生命) |
| **剑圣** | 吸血面罩(普攻伤害的 35% 转化为自身生命攻防转换30% 防御转攻击50% 几率降敌 5% 攻击 5 秒);梦魇肩甲;死亡守护 |
| **剑豪** | 空手接白刃(生命低于 55% 时永久降敌方全体 15% 物理伤害);梦魇肩甲;死亡守护;见闻色霸气(己方半场 +22% 击退抗性 / 敌方半场 +22% 控制抗性) |
| **酒仙** | 重拳(普攻随机降 6% 攻击或降 10% 防御 5 秒);元素抗性(血量首次低于 50%5 秒免疫物理或魔法伤害) |
| **哪吒** | 三味真火(攻击附加 3% 最大生命伤害,上限 150% 攻击);莲花护体(每损失 10% 生命 +10% 受伤回能) |
| **八神** | 狂暴之怒15% 伤害转化为自身生命);疯狂之血(血量低于 50%+5% 攻击 +5% 破甲) |
| **哀木涕** | 守护壁垒(开场获得 10% 最大生命护盾,自身与身后友军防御 +6%);战斗意志(每累计损失 20% 生命 +10% 受伤回能 +5% 格挡,最多 6 层) |
**通用觉醒技能**
- **梦魇肩甲**:受到攻击时,使攻击者 6 秒内对自己造成的伤害降低 2%(最多 3 层)[^4^]
- **死亡守护**:自身死亡时,为血量最低的友军回复 10% 最大生命值 [^4^]
---
## 四、其他英雄技能速览
| 英雄 | 定位 | 技能特点 |
|---|---|---|
| 骷髅王 | 前排/输出 | 大招对敌方全体施加诅咒 + 暴击伤害,通用性强 [^33^] |
| 月之女 | 输出 | 爆发能力极强,技能持续叠加伤害,每次释放输出更高 [^33^][^47^] |
| 潮壮壮 | 前排/控制 | 技能命中造成沉默让敌人无法放技能攻防兼备泛用性强PVP 必备且不太吃练度 [^33^][^47^] |
| 火枪 | 射手 | 「狙击」致命打击敌方后排,与复仇者斩击配合可双重秒杀后排 [^14^] |
| 船长 | 战士 | 幽灵船、舰船突袭:高额伤害 + 大幅击退,暴力推进队核心 [^14^] |
| 术士 | 辅助 | 地狱火有强大推进能力并可作前排分摊伤害;技能带减能、减疗、减速 [^14^] |
| 海拉 | 法师 | 技能带减能、减疗、沉默,控制折磨队核心 [^14^] |
| 沉默 | 辅助 | 技能带减能、沉默效果 [^14^] |
| 疾风忍者 | 前排 | 技能带减能、沉默效果 [^14^] |
| 铁扇公主 | 法师 | 推进 + 以自身为中心约 2 个人物身位的光环 [^68^] |
| 神罗战士 / 李白 | 战士 | 带真实伤害,无视防御,前排杀手 [^14^] |
| 人鱼公主 | 辅助 | 提供控制,并为全队增加攻击力加成,提升队伍输出 [^1^][^39^] |
| 莫莫 | 辅助 | 为队友提供减防支持,提升队伍生存与输出 [^1^] |
| 月光使者 | 辅助 | 黄武可用,开荒期过渡英雄 [^3^] |
| 爷爷(葫芦爷爷) | 辅助 | 为队友回能 + 加攻击,强化核心输出伤害 [^14^] |
| 暗器大师 / 影魔 | 射手 | 巫妖王体系最推荐的射手位 [^3^] |
---
## 五、英雄强度榜(参考)
综合多个攻略站的强度评价 [^33^][^37^][^34^]
- **T0**:复仇者、齐天大圣(另有榜单列入影刃、月之女)
- **T1**:骷髅王、月之女、绿巨人
- **T1.5**:剑圣
- **T2**:潮壮壮、暗器大师、矮人飞机、双头龙
- **T3**:光明骑士、术士(注:此榜为旧版评价,当前版本光明骑士、术士在特色阵容中仍有重要地位 [^14^]
**后期值得培养的主 C 推荐** [^1^]
- 战士:船长、黑暗骑士、复仇者、剑圣、神罗战士、白牛
- 辅助:冰女、小鹿、光明骑士、琴音仙女、术士、月光使者、沉默
- 射手:火枪、影魔、骷髅射手、暗器大师
- 法师:巫妖王、玉面狐妖、海拉、铁扇公主、深渊女王
---
## 参考来源
[^1^]: [几何王国游戏攻略/钓鱼/鱼缸搭配/英雄推荐 — 灯塔码铺](https://blog.share888.top/note/game/jihewangguo/index.html)
[^2^]: [《几何王国》——快速养成,满满成就!— 小米游戏中心](https://game.xiaomi.com/viewpoint/1585024444_1751347565577_100)
[^3^]: [《几何王国》该如何确定"终极"阵容和一些杂谈 — 雷电模拟器](https://www.ldmnq.com/0/zixun/1028749.html)
[^4^]: [几何王国英雄觉醒技能表 — 7724游戏](http://www.7724.com/jhwg/news/188085.html)
[^7^]: [【几何王国】英雄解析--剑豪 — 微信公众号](http://mp.weixin.qq.com/s?__biz=MzkxMjU1NTA2NA==&mid=2247485616&idx=1&sn=50f23d36776ee8d25f5916dee600caf7)
[^14^]: [《几何王国》新英雄与新玩法背景下,如何搭配适应版本的多样阵容 — 雷电模拟器](https://m.ldmnq.com/0/zixun/1028757.html)
[^18^]: [觉醒后英雄强度分析-前排篇I— 4399](https://a.4399.cn/gl/50173882_361641.html)
[^27^]: [【几何王国】英雄解析--至尊宝 — 微信公众号](http://mp.weixin.qq.com/s?__biz=MzkxMjU1NTA2NA==&mid=2247485630&idx=1&sn=5df2e8c053ab873e78ecfae5db7dfcb3)
[^28^]: [《几何王国》英雄攻略深渊女巫 — 百度贴吧](https://tieba.baidu.com/p/9105304613)
[^33^]: [几何王国角色强度排行 — 3DM手游](https://shouyou.3dmgame.com/gl/517771.html)
[^34^]: [几何王国角色排行表大全2025英雄强度排名 — 7724游戏](http://m.7724.com/jhwg/news/183870.html)
[^35^]: [几何王国社区 — 小米游戏中心](https://game.xiaomi.com/gamePost/62405456)
[^37^]: [几何王国角色强度排行 — 游侠手游](https://m.ali213.net/news/gl2404/1370787.html)
[^39^]: [《几何王国》新手进阶全攻略 — 百度文库](https://word.baidu.com/view/e9324c26b8d126fff705cc1755270722192e5990.html)
[^47^]: [几何王国最强英雄推荐 t0英雄强度排行 — 16手游网](http://www.16shouyou.com/gl/34278.html)
[^60^]: [深渊女王的三个阶段分别是什么?— 百度贴吧·几何王国吧](https://tieba.baidu.com/p/9646144609)
[^68^]: [《几何王国》觉醒后英雄强度分析-前排篇I— 雷电模拟器](https://m.ldmnq.com/0/zixun/1028753.html)
[^75^]: [几何王国绝顶英雄主推2024 — 悟极手游网](https://www.wjmplastics.com/article/223.html)

View File

@@ -0,0 +1,11 @@
{
"ver": "1.0.1",
"importer": "text",
"imported": true,
"uuid": "3ec17428-df2e-40bc-a707-4de99ee79b10",
"files": [
".json"
],
"subMetas": {},
"userData": {}
}

View File

@@ -122,7 +122,7 @@ export class Hero extends ecs.Entity {
model.grow_type = hero.grow_type;
model.role = hero.role;
model.fac = FacSet.HERO;
// 攻击距离配置优先未配置按攻击定位取默认值近战150 / 中程300 / 远程450
// 攻击距离配置优先未配置按攻击定位取默认值近战100 / 中程300 / 远程450
model.dis = hero.dis ?? HeroDisVal[hero.type as HType.Melee | HType.Mid | HType.Long];
model.posIndex = posIndex;
if (posIndex >= 0) {
@@ -133,7 +133,6 @@ export class Hero extends ecs.Entity {
model.call = hero.call;
model.dead = hero.dead;
model.fstart = hero.fstart;
model.fend = hero.fend;
model.atking = hero.atking;
model.atked = hero.atked;
model.field = hero.field;
@@ -172,7 +171,6 @@ export class Hero extends ecs.Entity {
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);

View File

@@ -17,6 +17,7 @@ import { mLogger } from "../common/Logger";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
import { getMonsterGoldDrop } from "../map/RogueConfig";
import { MissionEconomy } from "../map/MissionEconomy";
import { MoveComp } from "./MoveComp";
/** 最终伤害数据接口
* 用于封装一次攻击计算的所有结果数据
@@ -146,15 +147,15 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
}
}
mLogger.log(this.debugMode, 'HeroAtkSystem', " after crit", damage)
// 护盾吸收
// 护盾数值减免1 点护盾吸收 1 点伤害,扣完为止,溢出部分继续扣血)
const shieldResult = this.absorbShield(TAttrsComp, damage);
damage = shieldResult.remainingDamage;
if (shieldResult.absorbedDamage > 0) {
// 【评分系统 - 防御分】统计护盾成功抵挡伤害的次数
smc.vmdata.scores.shield_block_count += 1;
// 【评分系统 - 防御分】统计护盾累计吸收的伤害量
smc.vmdata.scores.shield_block_count += Math.floor(shieldResult.absorbedDamage);
}
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield", damage)
// 显示护盾格挡飘字
// 显示护盾减免飘字
if (shieldResult.absorbedDamage > 0 && targetView) {
targetView.shield_tip(shieldResult.absorbedDamage);
}
@@ -175,11 +176,6 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
TAttrsComp.atked_count++;
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
// 受击者产生击退效果
// if (damage > 0 && targetView) {
// targetView.back();
// }
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 英雄${TAttrsComp.hero_name} (uuid: ${TAttrsComp.hero_uuid}) 受到 eid:${casterEid} 的 伤害 ${damage},${isCrit ? "暴击" : "普通"}攻击,技能ID ${damageEvent.s_uuid}`);
// 冰冻判定
@@ -190,14 +186,26 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
const stunChance = (damageEvent.Attrs[Attrs.stun_chance] || 0) - TAttrsComp.getFinalStunRes();
const isStun = !TAttrsComp.isStun() && this.checkChance(stunChance);
// 击退判定
const knockbackChance = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.knockback_res || 0);
const isKnockback = this.checkChance(knockbackChance);
// 击退:受击默认后退,击退属性不再作为概率,而是转换为后退距离的增量
// 击退增量 = 施法者击退强化(原击退概率)- 被攻击者击退抗性 + 击退距离强化
const knockbackBonus = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.getFinalKnockbackRes() || 0);
const knockbackExtra = Math.max(0, knockbackBonus + (damageEvent.Attrs[Attrs.knockback_distance] || 0));
// ✅ 触发视图层表现(伤害数字、受击动画、冰冻、击晕、击退)
if (targetView) {
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, false);
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, knockbackExtra);
targetView.playEnd(skillConf.endAnm);
// 击退位移下沉到 MoveSystem直接写入 knockbackVelocity由 MoveSystem 每帧衰减并叠加位移
const moveComp = target.get(MoveComp);
if (moveComp) {
const dist = FightSet.BACK_RANG + knockbackExtra;
const isHero = TAttrsComp.fac === FacSet.HERO;
// 击退初速度0.1 秒时间常数衰减,总位移约等于 dist
const knockbackSpeed = dist * 16;
moveComp.knockbackVelocity = isHero ? -knockbackSpeed : knockbackSpeed;
}
if (isFrost) {
TAttrsComp.toFrost();
targetView.in_iced(FightSet.FROST_TIME);
@@ -212,9 +220,6 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
smc.vmdata.scores.stun_count++;
}
}
if (isKnockback) {
targetView.back(damageEvent.Attrs[Attrs.knockback_distance] || 0);
}
}
@@ -395,7 +400,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
* 用于所有概率相关的判定:
* - 暴击率判定
* - 闪避率判定
* - 击退概率判定
* - 冰冻/击晕概率判定
* - 其他特殊效果概率
*
* @param rate 概率值0-100的百分比
@@ -420,29 +425,26 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
/**
* 护盾吸收伤害
*
* 护盾吸收逻辑
* 1. 护盾值大于 0 时,抵挡本次伤害(免伤)
* 2. 每次抵挡消耗 1 点护盾值
* 3. 护盾归零时,重置护盾最大值属性
*
* 护盾数值减免伤害
*
* 护盾为数值减免型
* 1. 1 点护盾吸收 1 点伤害
* 2. 护盾扣减实际吸收量,溢出部分继续结算到血量
*
* @param TAttrsComp 被攻击者的属性组件(包含当前护盾值)
* @param damage 原始伤害值
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和被格挡的伤害值
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和被护盾减免的伤害值
*/
private absorbShield(TAttrsComp: HeroAttrsComp, damage: number): { remainingDamage: number, absorbedDamage: number } {
if (TAttrsComp.shield <= 0) {
mLogger.log(this.debugMode, 'HeroAtkSystem', " 护盾值小于等于0无法吸收伤害");
return { remainingDamage: damage, absorbedDamage: 0 };
}
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - 1);
if (TAttrsComp.shield <= 0) {
TAttrsComp.shield = 0;
}
const absorbed = Math.min(TAttrsComp.shield, damage);
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - absorbed);
TAttrsComp.dirty_shield = true;
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾抵挡1次伤害,剩余次数 ${TAttrsComp.shield}`);
return { remainingDamage: 0, absorbedDamage: damage };
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾减免 ${absorbed.toFixed(1)}伤害,剩余护盾 ${TAttrsComp.shield.toFixed(1)}`);
return { remainingDamage: damage - absorbed, absorbedDamage: absorbed };
}
/**

View File

@@ -47,7 +47,6 @@ export class HeroAttrsComp extends ecs.Comp {
[SkillTriggerType.Call]?: TriggerGrouped;
[SkillTriggerType.Dead]?: TriggerGrouped;
[SkillTriggerType.FStart]?: TriggerGrouped;
[SkillTriggerType.FEnd]?: TriggerGrouped;
[SkillTriggerType.Atking]?: TriggerGrouped;
[SkillTriggerType.Atked]?: TriggerGrouped;
/** 驻场光环(按 lv 解锁,存活期间全局生效) */
@@ -68,8 +67,6 @@ export class HeroAttrsComp extends ecs.Comp {
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
/** 当前等级下生效的战前触发条目 */
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
/** 当前等级下生效的战后触发条目 */
runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
/** 当前等级下生效的驻场光环词条列表 */
runtime_field?: FieldEntry[];
/** 当前等级下生效的复活配置 */
@@ -82,9 +79,9 @@ export class HeroAttrsComp extends ecs.Comp {
freeze_res: number = 0; // 冰冻抗性
stun_chance: number = 0; // 击晕概率
stun_res: number = 0; // 击晕抗性
knockback_chance: number = 0; // 击退概率
knockback_chance: number = 0; // 击退强化(原击退概率,现转换为击退距离增量)
knockback_distance: number = 0; // 击退距离强化
knockback_res: number = 0; // 击退抗性
knockback_res: number = 0; // 击退抗性(削减击退距离增量)
crit_damage: number = 0; // 额外暴击伤害
puncture_chance: number = 0; // 穿透概率
puncture_dmg_bonus: number = 0; // 穿透后伤害增量(百分点,乘算加成)
@@ -153,11 +150,11 @@ export class HeroAttrsComp extends ecs.Comp {
const addValue = Math.max(0, Math.floor(value));
if (addValue <= 0) return;
this.shield += addValue;
this.shield = Math.min(this.shield, FightSet.SHIELD_MAX); // 限制护盾最大层数
this.shield = Math.min(this.shield, FightSet.SHIELD_MAX); // 限制护盾值上限(数值减免型)
if (this.shield < 0) this.shield = 0;
this.dirty_shield = true; // 标记护盾需要更新
if (this.debugMode) {
mLogger.log(this.debugMode, 'HeroAttrs', ` 护盾次数变更: ${this.hero_name}, 变化=${addValue}, ${Math.floor(oldShield)} -> ${Math.floor(this.shield)}`);
mLogger.log(this.debugMode, 'HeroAttrs', ` 护盾变更: ${this.hero_name}, 变化=${addValue}, ${Math.floor(oldShield)} -> ${Math.floor(this.shield)}`);
}
}
add_hp_max(value: number) {
@@ -449,7 +446,7 @@ export class HeroAttrsComp extends ecs.Comp {
return base + mods.flatSum + mods.pctSum;
}
/** 最终击退概率 = 基础击退 + 限时修饰(加法模型) */
/** 最终击退强化 = 基础击退强化 + 限时修饰(加法模型,转换为击退距离增量 */
public getFinalKnockbackChance(): number {
const mods = this.aggregateTimedMods(Attrs.knockback_chance);
return this.knockback_chance + mods.flatSum + mods.pctSum;
@@ -580,7 +577,6 @@ export class HeroAttrsComp extends ecs.Comp {
this.call = undefined;
this.dead = undefined;
this.fstart = undefined;
this.fend = undefined;
this.atking = undefined;
this.atked = undefined;
this.field = undefined;
@@ -592,7 +588,6 @@ export class HeroAttrsComp extends ecs.Comp {
this.runtime_call = undefined;
this.runtime_dead = undefined;
this.runtime_fstart = undefined;
this.runtime_fend = undefined;
this.runtime_field = undefined;
this.runtime_revive = undefined;
this.critical = 0;

View File

@@ -0,0 +1,173 @@
import { _decorator, ProgressBar, Sprite, UIOpacity, Vec3, v3, tween, Tween, Color, clamp } 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 { HeroAttrsComp } from "./HeroAttrsComp";
import { FacSet, FightSet } from "../common/config/GameSet";
const { ccclass, property } = _decorator;
/**
* 英雄头顶状态栏组件
*
* 挂载在英雄节点下的 "top" 子节点上,独立管理:
* - 血条进度ProgressBar
* - 护盾数值条ProgressBartop/shield 节点,显示 shield / SHIELD_MAX
* - 透明度激活 / 闲置
* - 受击抖动动画
*
* 数据流HeroViewComp 将 HeroAttrsComp 引用传入,本组件通过 model 读取属性。
* 注意:英雄根节点下的 "shielded" 是护盾特效动画节点,由 HeroViewComp 管理,不在本组件职责内。
*/
@ccclass('HeroTopBarComp')
@ecs.register('HeroTopBar', false)
export class HeroTopBarComp extends CCComp {
// ==================== @property 绑定(编辑器拖拽) ====================
@property({ type: ProgressBar, tooltip: "血条进度条" })
private hpProgressBar: ProgressBar = null!;
@property({ type: ProgressBar, tooltip: "护盾数值条top 下的 shield 节点)" })
private shieldProgressBar: ProgressBar = null!;
@property({ type: Sprite, tooltip: "血条填充 Sprite用于设置阵营颜色" })
private hpBarSprite: Sprite = null!;
// ==================== 内部引用 ====================
private topOpacity: UIOpacity = null!;
private basePos: Vec3 = v3();
// ==================== 配置 ====================
private hp_height: number = 90;
private boss_hp_height: number = 160;
// ==================== 透明度 ====================
private readonly barIdleOpacity: number = 153;
private readonly barActiveOpacity: number = 255;
private readonly idleOpacityDelay: number = 0.25;
/** 外部赋值:英雄数据模型 */
private _model: HeroAttrsComp = null!;
public get model(): HeroAttrsComp { return this._model; }
public set model(value: HeroAttrsComp) { this._model = value; }
private readonly restoreBarIdleOpacity = () => {
this.setTopBarOpacity(false);
};
/**
* 初始化 / 对象池复用时重置
* @param model 英雄属性组件
* @param direction 显示方向1 或 -1
*/
public init(model: HeroAttrsComp, direction: number = 1): void {
this._model = model;
// UIOpacity 无 @property 绑定,运行时获取或添加
this.topOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
// 方向取 abs避免对象池复用时 scale 累乘导致血条反转
this.node.setScale(direction * Math.abs(this.node.scale.x), 1 * this.node.scale.y);
// 位置
const height = model.is_boss ? this.boss_hp_height : this.hp_height;
this.node.setPosition(0, height, 0);
this.basePos = this.node.position.clone();
// 血条颜色(英雄阵营为绿色)
if (model.fac === FacSet.HERO && this.hpBarSprite) {
this.hpBarSprite.color = new Color("#2ECC71");
}
// 确保血条等 UI 始终在最上层
this.node.setSiblingIndex(999);
// 重置显示状态
this.node.getChildByName("hp")!.active = true;
this.node.getChildByName("cd")!.active = false;
this.node.getChildByName("lv")!.active = false;
this.node.active = true;
this.setTopBarOpacity(false);
this.hpShow();
this.shieldShow(model.shield);
}
/** 更新血量进度条 */
public hpShow(): void {
if (!this._model || !this.hpProgressBar) return;
const hp = this._model.hp;
const hpMax = this._model.hp_max;
let targetProgress = hpMax > 0 ? hp / hpMax : 0;
targetProgress = clamp(targetProgress, 0, 1);
const prevProgress = this.hpProgressBar.progress;
if (targetProgress < prevProgress) {
this.activateTopBar();
this.playHpBarShake();
}
this.hpProgressBar.progress = targetProgress;
if (targetProgress > prevProgress) {
this.setTopBarOpacity(false);
}
}
/** 更新护盾数值条(百分比 = shield / SHIELD_MAX */
public shieldShow(shield: number = 0): void {
if (!this.shieldProgressBar) return;
const progress = FightSet.SHIELD_MAX > 0 ? shield / FightSet.SHIELD_MAX : 0;
this.shieldProgressBar.progress = clamp(progress, 0, 1);
// 护盾节点显隐
this.shieldProgressBar.node.active = shield > 0;
}
/** 激活顶栏(高亮 + 延迟恢复闲置透明度) */
public activateTopBar(): void {
this.setTopBarOpacity(true);
this.unschedule(this.restoreBarIdleOpacity);
this.scheduleOnce(this.restoreBarIdleOpacity, this.idleOpacityDelay);
}
/** 设置顶栏透明度 */
public setTopBarOpacity(isActive: boolean): void {
if (!this.node || !this.node.isValid) return;
if (this.topOpacity) {
this.topOpacity.opacity = isActive ? this.barActiveOpacity : this.barIdleOpacity;
}
this.node.active = true;
}
/** 显示顶栏 */
public show(): void {
if (this.node && this.node.isValid) this.node.active = true;
}
/** 隐藏顶栏 */
public hide(): void {
if (this.node && this.node.isValid) this.node.active = false;
}
/** 血条受击抖动动画 */
private playHpBarShake(): void {
if (!this.node || !this.node.isValid) return;
Tween.stopAllByTarget(this.node);
this.node.setPosition(this.basePos);
tween(this.node)
.by(0.04, { position: v3(-3, 0, 0) })
.by(0.04, { position: v3(6, 0, 0) })
.by(0.04, { position: v3(-5, 0, 0) })
.by(0.04, { position: v3(2, 0, 0) })
.call(() => {
this.node.setPosition(this.basePos);
})
.start();
}
/** 对象池回收 / 组件重置时调用 */
public reset(): void {
this.unscheduleAllCallbacks();
if (this.node && this.node.isValid) {
Tween.stopAllByTarget(this.node);
this.node.setPosition(this.basePos);
}
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "c4181fc5-1168-4a6d-8d03-d0f2dd0b22a2",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -1,8 +1,9 @@
import { Vec3, _decorator, v3, Collider2D, Contact2DType, Label, Node, Prefab, instantiate, ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Tween, Color, BoxCollider2D, UITransform, UIOpacity } from "cc";
import { Vec3, _decorator, v3, Collider2D, Contact2DType, Node, Prefab, instantiate, tween, Tween, BoxCollider2D, UITransform } 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 { mLogger } from "../common/Logger";
import { HeroSpine } from "./HeroSpine";
import { HeroTopBarComp } from "./HeroTopBarComp";
import { BoxSet, FacSet, FightSet, NumberFormatter, TooltipTypes } from "../common/config/GameSet";
import { smc } from "../common/SingletonModuleComp";
import { SkillSet, } from "../common/config/SkillSet";
@@ -37,21 +38,10 @@ export class HeroViewComp extends CCComp {
realDeadTime: number = 0.1
deadCD: number = 0
monDeadTime: number = 0.1
hp_height: number = 90
boss_hp_height: number = 160
// 血条显示相关
lastBarUpdateTime: number = 0; // 最后一次血条/蓝条/护盾更新时间
// ==================== UI 节点引用 ====================
private top_node: Node = null!;
private topOpacity: UIOpacity = null!;
private topBasePos: Vec3 = v3();
private lvLabel: Label | null = null; // 等级显示文本
private readonly barIdleOpacity: number = 153;
private readonly barActiveOpacity: number = 255;
private readonly idleOpacityDelay: number = 0.25;
private readonly restoreBarIdleOpacity = () => {
this.setTopBarOpacity(false);
};
private topBar: HeroTopBarComp = null!;
// ==================== 直接访问 HeroAttrsComp ====================
get model() {
@@ -96,67 +86,26 @@ export class HeroViewComp extends CCComp {
this.lastBarUpdateTime = 0;
this.status_change("idle");
// 初始化 UI 节点
this.initUINodes();
// 初始化头顶状态栏
const topNode = this.node.getChildByName("top");
this.topBar = topNode.getComponent(HeroTopBarComp) || topNode.addComponent(HeroTopBarComp);
this.topBar.init(this.model, this.scale);
/** 方向 */
this.node.setScale(this.scale * Math.abs(this.node.scale.x), 1 * this.node.scale.y); // 确保 scale.x 为正后再乘方向
// top_node 取 abs 避免对象池复用时 scale 累乘导致血条方向反转
this.top_node.setScale(this.scale * Math.abs(this.top_node.scale.x), 1 * this.top_node.scale.y);
/* 显示角色血*/
this.top_node.getChildByName("hp").active = true;
this.top_node.getChildByName("cd").active = false;
this.top_node.getChildByName("shield").active = false;
this.top_node.getChildByName("lv").active = true;
this.top_node.active = true;
this.setTopBarOpacity(false);
// 初始化等级显示
this.lv_show();
// 🔥 重置血条 UI 显示状态
// 🔥 重置描边
if (this.model) {
this.hp_show();
// 根据英雄模型中的等级数据设置描边
// 注意HeroViewComp 是挂在 node 上的,而 FlashSprite 可能挂在子节点(如 Sprite/anm 节点)
let flashSprite = this.node.getComponent(FlashSprite);
if (!flashSprite) {
// 如果当前节点没有,尝试在所有子节点中查找
flashSprite = this.node.getComponentInChildren(FlashSprite);
}
if (flashSprite) {
flashSprite.setOutlineByLevel(this.model.lv);
}
}
}
/** 初始化 UI 节点引用 */
private initUINodes() {
this.top_node = this.node.getChildByName("top");
this.topOpacity = this.top_node.getComponent(UIOpacity) || this.top_node.addComponent(UIOpacity);
this.top_node.setPosition(0, this.hp_height, 0);
if (this.model.is_boss) this.top_node.setPosition(0, this.boss_hp_height, 0);
this.topBasePos = this.top_node.position.clone();
const hpNode = this.top_node.getChildByName("hp");
if (this.model.fac == FacSet.HERO) {
hpNode.getChildByName("Bar").getComponent(Sprite).color = new Color("#2ECC71")
}
// 确保血条等UI始终在最上层显示
this.top_node.setSiblingIndex(999);
// 缓存等级显示 Labellv 节点下的 Label 子节点)
const lvNode = this.top_node.getChildByName("lv");
if (lvNode) {
this.lvLabel = lvNode.getChildByName("Label")?.getComponent(Label) ?? null;
}
}
/**
* View 层每帧更新
* 注意:数据更新逻辑已移到 HeroAttrSystem这里只负责显示
@@ -181,103 +130,21 @@ export class HeroViewComp extends CCComp {
// ✅ 按需更新 UI脏标签模式- 只在属性变化时更新
if (this.model.dirty_hp) {
this.hp_show();
this.topBar.hpShow();
this.model.dirty_hp = false;
}
if (this.model.dirty_shield) {
this.show_shield(this.model.shield);
this.topBar.shieldShow(this.model.shield);
this.model.dirty_shield = false;
}
if (this.model.dirty_lv) {
this.lv_show();
this.model.dirty_lv = false;
}
}
public cd_show() {
return;
}
/** 显示护盾 */
private show_shield(shield: number = 0) {
this.lastBarUpdateTime = Date.now() / 1000;
this.node.getChildByName("shielded").active = shield > 0;
}
/** 显示血量 */
private hp_show() {
this.lastBarUpdateTime = Date.now() / 1000;
// 不再基于血量是否满来决定显示状态,只更新进度条
let hp = this.model.hp;
let hp_max = this.model.hp_max;
// mLogger.log(this.debugMode, 'HeroViewComp', "hp_show",hp,hp_max)
let targetProgress = hp_max > 0 ? hp / hp_max : 0;
targetProgress = clamp(targetProgress, 0, 1);
let hpNode = this.top_node.getChildByName("hp");
let hpProgressBar = hpNode.getComponent(ProgressBar);
const prevProgress = hpProgressBar.progress;
if (targetProgress < hpProgressBar.progress) {
this.activateTopBar();
this.playHpBarShake();
}
hpProgressBar.progress = targetProgress;
if (targetProgress > prevProgress) {
this.setTopBarOpacity(false);
}
}
private isFullHpAndNoShield(): boolean {
if (!this.model) return false;
if (this.model.hp_max <= 0) return false;
return this.model.hp >= this.model.hp_max;
}
/** 更新等级显示 */
private lv_show() {
if (!this.lvLabel || !this.model) return;
this.lvLabel.string = String(this.model.lv);
}
private setTopBarOpacity(isActive: boolean) {
if (!this.top_node || !this.top_node.isValid) return;
if (this.topOpacity) {
this.topOpacity.opacity = this.barActiveOpacity;
}
if (isActive) {
this.top_node.active = true;
return;
}
this.top_node.active = true;
}
private activateTopBar() {
this.setTopBarOpacity(true);
this.unschedule(this.restoreBarIdleOpacity);
this.scheduleOnce(this.restoreBarIdleOpacity, this.idleOpacityDelay);
}
private playHpBarShake() {
if (!this.top_node || !this.top_node.isValid) return;
Tween.stopAllByTarget(this.top_node);
this.top_node.setPosition(this.topBasePos);
tween(this.top_node)
.by(0.04, { position: v3(-3, 0, 0) })
.by(0.04, { position: v3(6, 0, 0) })
.by(0.04, { position: v3(-5, 0, 0) })
.by(0.04, { position: v3(2, 0, 0) })
.call(() => {
this.top_node.setPosition(this.topBasePos);
})
.start();
}
/** 升级特效 */
public lv_up() {
this.spawnTimedFx("game/skill/buff/buff_lvup", this.node, 1.0);
@@ -349,7 +216,7 @@ export class HeroViewComp extends CCComp {
Tooltip.load(pos, type, value, s_uuid, this.node.parent, 1, this.model?.fac ?? FacSet.MON);
}
/** 护盾格挡提示 */
/** 护盾减免提示 */
shield_tip(absorbed: number) {
this.hp_tip(TooltipTypes.shield, NumberFormatter.formatNumber(Math.max(0, Math.floor(absorbed))));
}
@@ -432,7 +299,7 @@ export class HeroViewComp extends CCComp {
}
add_shield(shield: number) {
// 护盾数据更新由 Model 层处理,这里只负责视图表现
if (this.model && this.model.shield > 0) this.show_shield(this.model.shield);
if (this.model && this.model.shield > 0) this.topBar.shieldShow(this.model.shield);
}
health(hp: number = 0) {
@@ -458,7 +325,7 @@ export class HeroViewComp extends CCComp {
}
// 恢复UI
this.top_node.active = true;
this.topBar.show();
this.status_change("idle");
@@ -504,7 +371,7 @@ export class HeroViewComp extends CCComp {
}
// 隐藏UI
this.top_node.active = false;
this.topBar.hide();
// 在销毁实体前先禁用碰撞体,从源头减少"尸体"参与碰撞
const collider = this.node.getComponent(Collider2D);
@@ -525,9 +392,10 @@ export class HeroViewComp extends CCComp {
})
.start();
}
do_atked(damage: number, isCrit: boolean, s_uuid: number, isBack: boolean = false) {
/** 受击表现(伤害数字、受击动画、击退) */
do_atked(damage: number, isCrit: boolean, s_uuid: number, knockbackExtra: number = 0) {
// 受到攻击时更新最后更新时间
this.activateTopBar();
this.topBar.activateTopBar();
this.lastBarUpdateTime = Date.now() / 1000;
if (damage <= 0) return;
@@ -542,43 +410,15 @@ export class HeroViewComp extends CCComp {
// 视图层表现
let SConf = SkillSet[s_uuid]
const hitAnm = SConf?.DAnm || "atked";
if (isBack) this.back()
this.back(knockbackExtra)
this.in_atked(hitAnm, this.model.fac == FacSet.HERO ? 1 : -1);
this.showDamage(damage, isCrit);
}
private isBackingUp: boolean = false; // 🔥 添加后退状态标记
//后退
back(distance: number = 0) {
// 🔥 防止重复调用后退动画
if (this.isBackingUp) return;
this.isBackingUp = true; // 🔥 设置后退状态
if (this.model.fac == FacSet.MON) {
// 基础击退距离,加上额外的距离强化
let dist = FightSet.BACK_RANG + distance;
let tx = this.node.position.x + dist;
if (tx > 320) tx = 320;
tween(this.node)
.to(0.1, { position: v3(tx, this.node.position.y, 0) })
.call(() => {
this.isBackingUp = false; // 🔥 动画完成后重置状态
})
.start();
}
if (this.model.fac == FacSet.HERO) {
let dist = 5 + distance;
let tx = this.node.position.x - dist;
if (tx < -320) tx = -320;
tween(this.node)
.to(0.1, { position: v3(tx, this.node.position.y, 0) })
.call(() => {
this.isBackingUp = false; // 🔥 动画完成后重置状态
})
.start();
}
// 击退位移由 MoveSystem 统一处理,视图层只负责触发,不直接操作 position
// 具体击退速度由 HeroAtkSystem 在伤害结算后写入 MoveComp.knockbackVelocity
}
// 伤害计算和战斗逻辑已迁移到 HeroBattleSystem
@@ -629,7 +469,7 @@ export class HeroViewComp extends CCComp {
if (!this.model) return;
const damageText = NumberFormatter.formatNumber(Math.max(0, Math.floor(damage)));
this.hp_show();
this.topBar.hpShow();
if (isCrit) {
this.hp_tip(TooltipTypes.crit, damageText);
} else {
@@ -640,10 +480,7 @@ export class HeroViewComp extends CCComp {
// 清理残留的定时器和缓动
this.unscheduleAllCallbacks();
Tween.stopAllByTarget(this.node);
if (this.top_node && this.top_node.isValid) {
Tween.stopAllByTarget(this.top_node);
this.top_node.setPosition(this.topBasePos);
}
this.topBar.reset();
// 清理碰撞器事件监听
const collider = this.getComponent(Collider2D);
@@ -652,7 +489,6 @@ export class HeroViewComp extends CCComp {
}
this.deadCD = 0
this.lastBarUpdateTime = 0
this.unschedule(this.restoreBarIdleOpacity);
// 清理伤害队列
this.damageQueue.length = 0;

View File

@@ -148,8 +148,9 @@ export class Monster extends ecs.Entity {
(node as any)._baseScale = node.scale.clone();
}
// 统一挂到实体显示层 HERO 节点下
node.parent = scene.entityLayer!.node!.getChildByName("HERO")!;
// 按身份挂载显示层Boss → BOSS 层(最底),小怪 → MON 层(编辑器已配置好层级顺序)
const layerName = is_boss ? "BOSS" : "MON";
node.parent = scene.entityLayer!.node!.getChildByName(layerName)!;
var view = node.getComponent(HeroViewComp)!;
const collider = node.getComponent(BoxCollider2D);
// 入场期间关闭碰撞,防止下落时提前参与战斗
@@ -174,7 +175,7 @@ export class Monster extends ecs.Entity {
model.speed = hero.speed ?? 800;
model.type = hero.type;
model.fac = FacSet.MON;
// 攻击距离配置优先未配置按攻击定位取默认值近战150 / 中程300 / 远程450
// 攻击距离配置优先未配置按攻击定位取默认值近战100 / 中程300 / 远程450
model.dis = hero.dis ?? HeroDisVal[hero.type as HType.Melee | HType.Mid | HType.Long];
model.posIndex = laneIndex;
if (laneIndex >= 0) {
@@ -185,7 +186,6 @@ export class Monster extends ecs.Entity {
model.call = hero.call;
model.dead = hero.dead;
model.fstart = hero.fstart;
model.fend = hero.fend;
model.atking = hero.atking;
model.atked = hero.atked;
model.revive = hero.revive;
@@ -224,7 +224,6 @@ export class Monster extends ecs.Entity {
if (testSkills.atked !== undefined) model.atked = testSkills.atked;
if (testSkills.dead !== undefined) model.dead = testSkills.dead;
if (testSkills.fstart !== undefined) model.fstart = testSkills.fstart;
if (testSkills.fend !== undefined) model.fend = testSkills.fend;
if (testSkills.call !== undefined) model.call = testSkills.call;
if (testSkills.revive !== undefined) model.revive = testSkills.revive;
}
@@ -233,7 +232,6 @@ export class Monster extends ecs.Entity {
model.runtime_call = resolveTriggerByLv(model.call, mon_lv);
model.runtime_dead = resolveTriggerByLv(model.dead, mon_lv);
model.runtime_fstart = resolveTriggerByLv(model.fstart, mon_lv);
model.runtime_fend = resolveTriggerByLv(model.fend, mon_lv);
model.runtime_atking = resolveTriggerByLv(model.atking, mon_lv);
model.runtime_atked = resolveTriggerByLv(model.atked, mon_lv);
model.runtime_revive = resolveReviveByLv(model.revive, mon_lv);

View File

@@ -28,6 +28,8 @@ export class MoveComp extends ecs.Comp {
baseY: number = 0;
/** 出生序,用于同条件渲染排序稳定 */
spawnOrder: number = 0;
/** 击退速度(像素/秒),受击瞬间由 HeroAtkSystem 赋值MoveSystem 每帧指数衰减至 0 */
knockbackVelocity: number = 0;
reset() {
this.direction = 1;
@@ -35,6 +37,7 @@ export class MoveComp extends ecs.Comp {
this.moving = true;
this.baseY = 0;
this.spawnOrder = 0;
this.knockbackVelocity = 0;
}
}
@@ -82,11 +85,25 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return;
}
/** 所有移动都锁定在 baseY避免出现漂移 */
/** 所有移动都锁定在 baseY避免出现"漂移" */
if (view.node.position.y !== move.baseY) {
view.node.setPosition(view.node.position.x, move.baseY, 0);
}
/** 击退位移:每帧叠加 knockbackVelocity 并指数衰减,与推进解耦 */
if (Math.abs(move.knockbackVelocity) > 0.01) {
const newX = view.node.position.x + move.knockbackVelocity * this.dt;
const clampedX = this.clampByFacBoundary(newX, model.fac);
if (Math.abs(clampedX - view.node.position.x) > 0.01) {
view.node.setPosition(clampedX, view.node.position.y, 0);
}
// 时间常数 τ=0.1s0.1 秒衰减到约 37%0.3 秒衰减到约 5%,总位移 ≈ v0 * 0.0632
move.knockbackVelocity *= Math.exp(-10 * this.dt);
if (Math.abs(move.knockbackVelocity) < 0.01) {
move.knockbackVelocity = 0;
}
}
/** 准备阶段:双方原地待命,不推进不索敌 */
if (!smc.mission.in_fight) {
this.clearCombatTarget(model);
@@ -136,21 +153,18 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return dist <= attackRange;
}
/** 同阵营横向最小间距,防止停止移动时完全重叠 */
private static readonly ALLY_MIN_GAP_X = 10;
/** 挤位时的蠕动幅度(像素) */
private static readonly JOSTLE_AMPLITUDE = 2;
/**
* 战斗位移:向最近敌人推进,进入攻击范围后停止。
* 推进无上限限制,可穿越队友;停止移动后受同阵营最小间距约束
* 推进无上限限制,但击退/位移受阵营后退边界保护
*/
private processCombatLogic(_e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
const selfX = view.node.position.x;
const enemyX = enemy.node.position.x;
const dist = Math.abs(selfX - enemyX);
const attackRange = model.dis;
const inRange = dist <= attackRange;
/** 近战贴身作战:攻击距离 dis(100) 大于技能范围(60),推进到与敌人相隔 60 才停下,保证技能可命中且不过度贴身 */
const stopRange = model.type === 0 ? 60 : attackRange;
const inRange = dist <= stopRange;
if (inRange) {
/** 进入攻击范围:停止移动,进入攻击状态,由 SCastSystem 负责技能释放 */
@@ -160,10 +174,8 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (view.status === "move") {
view.status_change("idle");
}
/** 停止时若与队友重叠,则向后微调保持最小间距,并加入蠕动避免死板 */
this.adjustSpacingWhenIdle(_e, view, move, model);
} else {
/** 未接触:向敌人方向推进(可穿越队友,但同点时后来者稍停) */
/** 未接触:向敌人方向推进 */
model.is_atking = false;
const dir = enemyX > selfX ? 1 : -1;
move.direction = dir;
@@ -171,9 +183,6 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const delta = speed * this.dt * dir;
let newX = selfX + delta;
/** 推进时若与队友同点,后来者稍停避让 */
newX = this.adjustSpacingWhileMoving(_e, view, move, model, newX, dir);
/** 后退边界钳制:防止被击退/位移推出屏幕 */
newX = this.clampByFacBoundary(newX, model.fac);
@@ -184,93 +193,6 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
/**
* 推进时的间距避让。
* Why: 允许穿越队友,但若与队友完全同点(重叠),后来者稍停一拍,
* 避免视觉上的"穿模"过于生硬,营造"挤过去"的自然感。
* @returns 调整后的目标 X被阻挡时返回 selfX即本帧不移动
*/
private adjustSpacingWhileMoving(self: ecs.Entity, view: HeroViewComp, selfMove: MoveComp, model: HeroAttrsComp, targetX: number, _dir: number): number {
const selfX = view.node.position.x;
const selfY = view.node.position.y;
let blocked = false;
ecs.query(this.getHeroViewMatcher()).forEach(e => {
if (e === self) return;
const attrs = e.get(HeroAttrsComp);
const v = e.get(HeroViewComp);
const mv = e.get(MoveComp);
if (!attrs || !v?.node || !mv) return;
if (attrs.is_dead || attrs.is_reviving) return;
if (attrs.fac !== model.fac) return;
if (Math.abs(v.node.position.y - selfY) > 30) return;
const otherX = v.node.position.x;
/** 同点判定:间距小于最小间距的一半视为完全重叠 */
if (Math.abs(selfX - otherX) < MoveSystem.ALLY_MIN_GAP_X * 0.5) {
/** spawnOrder 大者(后来者)被阻挡 */
if (selfMove.spawnOrder > mv.spawnOrder) {
blocked = true;
}
}
});
return blocked ? selfX : targetX;
}
/**
* 停止移动时的间距微调 + 蠕动效果。
* Why: 允许推进时穿越队友,但进入攻击范围停下后若与队友重叠,
* 则向后微调保持 ALLY_MIN_GAP_X 间距,避免视觉完全重叠。
* 同时加入基于时间的随机蠕动,营造"挤到前方攻击"的生动感。
* Y 轴不变,仅约束 X。
* 优先级spawnOrder 小者(先出生)不动,大者向后让位。
*/
private adjustSpacingWhenIdle(self: ecs.Entity, view: HeroViewComp, selfMove: MoveComp, model: HeroAttrsComp): void {
const selfX = view.node.position.x;
const selfY = view.node.position.y;
let adjustX = selfX;
let isCrowded = false;
ecs.query(this.getHeroViewMatcher()).forEach(e => {
if (e === self) return;
const attrs = e.get(HeroAttrsComp);
const v = e.get(HeroViewComp);
const mv = e.get(MoveComp);
if (!attrs || !v?.node || !mv) return;
if (attrs.is_dead || attrs.is_reviving) return;
if (attrs.fac !== model.fac) return;
/** 只约束同排Y 差在阈值内)的队友 */
if (Math.abs(v.node.position.y - selfY) > 30) return;
const otherX = v.node.position.x;
const gap = Math.abs(adjustX - otherX);
if (gap >= MoveSystem.ALLY_MIN_GAP_X) return;
isCrowded = true;
/** spawnOrder 小者不动,大者向后让位 */
if (selfMove.spawnOrder > mv.spawnOrder) {
/** 自己靠后:向远离对方的方向后退到最小间距处 */
const dir = adjustX >= otherX ? 1 : -1;
adjustX = otherX + dir * MoveSystem.ALLY_MIN_GAP_X;
}
});
/** 拥挤时加入蠕动:基于时间+出生序的伪随机偏移,避免多个单位同步抖动 */
if (isCrowded) {
const time = Date.now() / 1000;
const jostlePhase = time * 3 + selfMove.spawnOrder * 0.7; // 不同单位错开相位
const jostleOffset = Math.sin(jostlePhase) * MoveSystem.JOSTLE_AMPLITUDE;
adjustX += jostleOffset;
}
if (Math.abs(adjustX - selfX) >= 0.01) {
/** 后退边界钳制 */
adjustX = this.clampByFacBoundary(adjustX, model.fac);
view.node.setPosition(adjustX, view.node.position.y, 0);
}
}
/**
* 按阵营裁剪 X 坐标,防止被击退/位移推出屏幕。
* HERO 后退不越过 BoxSet.LETF_ENDMON 后退不越过 BoxSet.RIGHT_END。
@@ -313,13 +235,19 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
private updateRenderOrder() {
const scene = smc.map?.MapView?.scene;
const actorRoot = scene?.entityLayer?.node?.getChildByName("HERO");
if (!actorRoot) return;
const layerNode = scene?.entityLayer?.node;
if (!layerNode) return;
const now = Date.now() / 1000;
if (now - this.lastRenderSortAt < this.renderSortInterval) return;
this.lastRenderSortAt = now;
const heroRoot = layerNode.getChildByName("HERO");
const monRoot = layerNode.getChildByName("MON");
const bossRoot = layerNode.getChildByName("BOSS");
/** 分层重排同层节点:各阵营层内按 Y 轴路次 -> 前后位置 -> 出生序 -> eid 排序
* Boss 已有独立 BOSS 层固定在最底,无需 bossPriority 参与排序) */
this.renderEntryCount = 0;
ecs.query(this.getHeroViewMatcher()).forEach(e => {
const attrs = e.get(HeroAttrsComp);
@@ -327,8 +255,16 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (!attrs || !actorView?.node || attrs.is_dead) return;
if (attrs.fac !== FacSet.HERO && attrs.fac !== FacSet.MON) return;
if (actorView.node.parent !== actorRoot) {
actorView.node.parent = actorRoot;
/** 按阵营/身份归位到对应显示层 */
let targetRoot: Node | null | undefined;
if (attrs.fac === FacSet.HERO) {
targetRoot = heroRoot;
} else {
targetRoot = attrs.is_boss ? (bossRoot ?? monRoot ?? heroRoot) : (monRoot ?? heroRoot);
}
if (!targetRoot) return;
if (actorView.node.parent !== targetRoot) {
actorView.node.parent = targetRoot;
}
/** 统一从 MoveComp 获取 spawnOrder */
@@ -347,7 +283,6 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
this.renderEntries.push(entry);
}
entry.node = actorView.node;
entry.bossPriority = attrs.is_boss ? 1 : 0;
entry.laneScore = laneScore;
entry.frontScore = frontScore;
entry.spawnOrder = spawnOrder;
@@ -356,9 +291,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
});
this.renderEntries.length = this.renderEntryCount;
/** 定时重排同层节点Boss 优先级 -> Y 轴路次 -> 前后位置 -> 出生序 -> eid */
this.renderEntries.sort((a, b) => {
if (a.bossPriority !== b.bossPriority) return a.bossPriority - b.bossPriority;
// 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;
@@ -366,8 +299,14 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return a.eid - b.eid;
});
this.renderEntries.forEach((item, index) => {
/** 分层应用 siblingIndex各层内部从 0 递增 */
const layerCounters = new Map<Node, number>();
this.renderEntries.forEach((item) => {
const parent = item.node.parent;
if (!parent) return;
const index = layerCounters.get(parent) ?? 0;
item.node.setSiblingIndex(index);
layerCounters.set(parent, index + 1);
});
}
}

View File

@@ -44,9 +44,6 @@ export class SkillTriggerHelper {
case SkillTriggerType.FStart:
this.handleArrayTrigger(model.runtime_fstart, model, view, type);
break;
case SkillTriggerType.FEnd:
this.handleArrayTrigger(model.runtime_fend, model, view, type);
break;
case SkillTriggerType.Atking:
this.handleAtking(model, view);
break;
@@ -142,7 +139,7 @@ export class SkillTriggerHelper {
}
/**
* 通用的数组型触发器处理(适用于 FStart / FEnd 等无需额外判定的简单列表触发)
* 通用的数组型触发器处理(适用于 FStart 等无需额外判定的简单列表触发)
*/
private static handleArrayTrigger(configs: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[] | undefined, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
if (!configs || configs.length === 0) return;

View File

@@ -478,7 +478,6 @@ export class CHeroComp extends CCComp {
call: hero.call,
dead: hero.dead,
fstart: hero.fstart,
fend: hero.fend,
atking: hero.atking,
atked: hero.atked,
field: hero.field,

View File

@@ -13,7 +13,6 @@
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每回合次数)
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
* - FightEnd (5):监听 FightEnd 事件(每回合结束派发),按 trigger_limit 全局累计上限
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
* - HeroCall (7):监听 MasterCalled主角/技能召唤)+ ReviveSuccess复活
*
@@ -145,7 +144,6 @@ export class EquipBoxComp extends CCComp {
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
oops.message.off(GameEvent.FightStart, this.onFightStart, this);
oops.message.off(GameEvent.FightStart, this.onEventTrigger, this);
oops.message.off(GameEvent.FightEnd, this.onEventTrigger, this);
oops.message.off(GameEvent.HeroDead, this.onEventTrigger, this);
oops.message.off(GameEvent.MasterCalled, this.onEventTrigger, this);
oops.message.off(GameEvent.ReviveSuccess, this.onEventTrigger, this);
@@ -287,7 +285,6 @@ export class EquipBoxComp extends CCComp {
* - Interval: 监听 FightStart进入战斗后由 update 帧驱动计时
* - Field: 不主动施法(实际生效由 FieldSkillSet 处理)
* - FightStart: 监听 FightStart 事件
* - FightEnd: 监听 FightEnd 事件
* - HeroDead: 监听 HeroDead 事件(已在派发处做阵营过滤)
* - HeroCall: 监听 MasterCalled主角/技能召唤)+ ReviveSuccess复活
*
@@ -313,10 +310,6 @@ export class EquipBoxComp extends CCComp {
oops.message.on(GameEvent.FightStart, this.onEventTrigger, this);
break;
case CardTriggerType.FightEnd:
oops.message.on(GameEvent.FightEnd, this.onEventTrigger, this);
break;
case CardTriggerType.HeroDead:
oops.message.on(GameEvent.HeroDead, this.onEventTrigger, this);
break;
@@ -338,7 +331,7 @@ export class EquipBoxComp extends CCComp {
* 事件型触发的统一入口:
* - Instant 类型:按 trigger_times 上限判定,复用 current_trigger_times 跟踪
* (保持与原 is_instant 行为一致,且 NewWave 中也用 current_trigger_times
* - 事件型FightStart/FightEnd/HeroDead/HeroCall按 trigger_limit 上限判定,使用 trigger_count 跟踪
* - 事件型FightStart/HeroDead/HeroCall按 trigger_limit 上限判定,使用 trigger_count 跟踪
*
* 注意:本方法不读取事件 payload仅作触发信号使用避免 MasterCalled 不同 payload 字段引发的兼容问题)。
*/
@@ -445,7 +438,6 @@ export class EquipBoxComp extends CCComp {
const showRemainCount =
this.trigger_type === CardTriggerType.Interval ||
this.trigger_type === CardTriggerType.FightStart ||
this.trigger_type === CardTriggerType.FightEnd ||
this.trigger_type === CardTriggerType.HeroDead ||
this.trigger_type === CardTriggerType.HeroCall;
@@ -454,7 +446,6 @@ export class EquipBoxComp extends CCComp {
// 事件型按 trigger_limitInterval 按 t_times
const isEvent =
this.trigger_type === CardTriggerType.FightStart ||
this.trigger_type === CardTriggerType.FightEnd ||
this.trigger_type === CardTriggerType.HeroDead ||
this.trigger_type === CardTriggerType.HeroCall;
const used = isEvent ? this.trigger_count : this.current_trigger_times;
@@ -514,7 +505,7 @@ export class EquipBoxComp extends CCComp {
* 处理维持回合逻辑:递减剩余回合,或者重置触发次数。
*
* 各类型在新一回合的行为:
* - Instant/Interval/FightStart/FightEnd:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
* - Instant/Interval/FightStart按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
* - Field被动生效跟随 keep_waves 决定存活
* - HeroDead/HeroCall跨回合触发的事件型trigger_count全局不重置仅 keep_waves 控制存活
*/

View File

@@ -217,7 +217,6 @@ export class HeroBoxComp extends CCComp {
call: hero.call,
dead: hero.dead,
fstart: hero.fstart,
fend: hero.fend,
atking: hero.atking,
atked: hero.atked,
field: hero.field,

View File

@@ -61,10 +61,10 @@ export class HeroMoreCom extends Component {
/** 冰冻率附加值 */
@property(Label)
private freeze_plus_label: Label = null;
/** 击退总值(百分点 */
/** 击退强化总值(击退距离增量 */
@property(Label)
private knockback_all_label: Label = null;
/** 击退附加值 */
/** 击退强化附加值 */
@property(Label)
private knockback_plus_label: Label = null;
/** 穿透率总值(百分点) */
@@ -177,12 +177,13 @@ export class HeroMoreCom extends Component {
const finalCritDmg = this.model.getFinalCritDamage();
this.setStatRow(this.crit_dmg_all_label, this.crit_dmg_plus_label, finalCritDmg, finalCritDmg - (this.model.crit_damage ?? 0), "%");
// ---- 冰冻率 / 击退 / 穿透率 / 风怒率(百分点) ----
// ---- 冰冻率 / 击退强化 / 穿透率 / 风怒率 ----
const finalFreeze = this.model.getFinalFreezeChance();
this.setStatRow(this.freeze_all_label, this.freeze_plus_label, finalFreeze, finalFreeze - (this.model.freeze_chance ?? 0), "%");
// 击退强化已改为击退距离增量,不再按百分比显示
const finalKnockback = this.model.getFinalKnockbackChance();
this.setStatRow(this.knockback_all_label, this.knockback_plus_label, finalKnockback, finalKnockback - (this.model.knockback_chance ?? 0), "%");
this.setStatRow(this.knockback_all_label, this.knockback_plus_label, finalKnockback, finalKnockback - (this.model.knockback_chance ?? 0), "");
const finalPuncture = this.model.getFinalPunctureChance();
this.setStatRow(this.puncture_all_label, this.puncture_plus_label, finalPuncture, finalPuncture - (this.model.puncture_chance ?? 0), "%");

View File

@@ -2264,7 +2264,6 @@ export class MissionCardComp extends CCComp {
model.runtime_call = resolveTriggerByLv(model.call, nextLv);
model.runtime_dead = resolveTriggerByLv(model.dead, nextLv);
model.runtime_fstart = resolveTriggerByLv(model.fstart, nextLv);
model.runtime_fend = resolveTriggerByLv(model.fend, nextLv);
model.runtime_atking = resolveTriggerByLv(model.atking, nextLv);
model.runtime_atked = resolveTriggerByLv(model.atked, nextLv);
model.runtime_field = resolveFieldByLv(model.field, nextLv);

View File

@@ -225,7 +225,7 @@ export class MissionComp extends CCComp {
protected update(dt: number): void {
if (!smc.mission.play) return
// 如果是暂停状态,且不在 BattleEnd 阶段(全灭时需要播放完 fend 技能动画并自动流转),才真正停止 update 逻辑
// 如果是暂停状态,且不在 BattleEnd 阶段(全灭时需要自动流转),才真正停止 update 逻辑
if (smc.mission.pause && this.currentPhase !== MissionPhase.BattleEnd) return
// 回合间倒计时PrepareStart 复用为倒计时阶段5 秒后自动进入下一回合
@@ -515,7 +515,7 @@ export class MissionComp extends CCComp {
case MissionPhase.BattleStart:
// 触发战斗开始技能fstart
this.triggerHeroBattleSkills(true);
this.triggerHeroBattleSkills();
// Boss 预警Boss 同步场立即登场eta=0
if (this.isBossWave) {
oops.message.dispatchEvent(GameEvent.BossWarning, {
@@ -576,14 +576,9 @@ export class MissionComp extends CCComp {
smc.vmdata.scores.passed_wave_20 = true;
}
// 触发战斗结束技能fend
this.triggerHeroBattleSkills(false);
// 战斗结束阶段,按 FightSet.WAVE_HEAL_RATE 恢复所有英雄血量
this.healAllHeroes();
// 【新增】派发每回合战斗结束事件,供卡牌技能监听(区别于整局结束的 MissionEnd
oops.message.dispatchEvent(GameEvent.FightEnd);
break;
case MissionPhase.Settle:
@@ -669,32 +664,26 @@ export class MissionComp extends CCComp {
* - 标记非战斗
* - 暂停刷怪
* - 显示开始按钮
* - 触发英雄战斗结束技能
*/
private enterPreparePhase() {
this.changePhase(MissionPhase.PrepareStart);
}
/**
* 触发英雄的战斗开始/结束技能
* @param isStart 是否为战斗开始
* 触发英雄的战斗开始技能
*/
private triggerHeroBattleSkills(isStart: boolean) {
private triggerHeroBattleSkills() {
let triggerCount = 1;
if (isStart) {
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.StartCount);
} else {
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.EndCount);
}
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.StartCount);
triggerCount = Math.max(1, Math.floor(triggerCount));
ecs.query(this.heroAttrsMatcher).forEach(entity => {
const attrs = entity.get(HeroAttrsComp);
const view = entity.get(HeroViewComp);
if (!attrs || !view || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
// 触发战斗开始/结束技能
// 触发战斗开始技能
for (let i = 0; i < triggerCount; i++) {
SkillTriggerHelper.trigger(isStart ? SkillTriggerType.FStart : SkillTriggerType.FEnd, attrs, view);
SkillTriggerHelper.trigger(SkillTriggerType.FStart, attrs, view);
}
});
}
@@ -755,8 +744,7 @@ export class MissionComp extends CCComp {
open_Victory(e: any, is_hero_dead: boolean = false) {
// 战斗失败或胜利,标记暂停状态以切断回合流转逻辑
smc.mission.pause = true;
// 直接切入 BattleEnd触发 fend 表现
// 倒计时逻辑已在 update 中由 PhaseTime 接管2s 后将触发 autoNextPhase 弹窗结算
// 直接切入 BattleEnd2s 后由 autoNextPhase 自动流转弹窗结算
this.changePhase(MissionPhase.BattleEnd);
}
@@ -859,7 +847,7 @@ export class MissionComp extends CCComp {
this.isBossWave = true; // 单场 Boss 战,恒为 Boss 战(超时阈值/预警走 Boss 档)
if (wave > 1) {
// 在新一回合到来时,先进入 BattleEnd触发上一回合的战斗结束技能 (fend)2秒后自动进入下一回合的准备阶段
// 在新一回合到来时,先进入 BattleEnd2秒后自动进入下一回合的准备阶段
this.changePhase(MissionPhase.BattleEnd);
} else {
// 第1回合不需要结束上一回合延迟一点播放提示避免被开始游戏的loading遮挡
@@ -1005,21 +993,17 @@ export class MissionComp extends CCComp {
this.clearBattleSceneNodes();
}
/** 清理战斗场景中的 HERO 和 SKILL 根节点下的所有子节点 */
/** 清理战斗场景中的 HERO/MON/BOSS 和 SKILL 根节点下的所有子节点 */
private clearBattleSceneNodes() {
const scene = smc.map?.MapView?.scene;
const layer = scene?.entityLayer?.node;
if (!layer) return;
const heroRoot = layer.getChildByName("HERO");
const skillRoot = layer.getChildByName("SKILL");
if (heroRoot) {
for (let i = heroRoot.children.length - 1; i >= 0; i--) {
heroRoot.children[i].destroy();
}
}
if (skillRoot) {
for (let i = skillRoot.children.length - 1; i >= 0; i--) {
skillRoot.children[i].destroy();
for (const name of ["HERO", "MON", "BOSS", "SKILL"]) {
const root = layer.getChildByName(name);
if (root) {
for (let i = root.children.length - 1; i >= 0; i--) {
root.children[i].destroy();
}
}
}
}

View File

@@ -142,7 +142,7 @@ export class MissionMonCompComp extends CCComp {
const spawnX = MissionMonCompComp.SPAWN_X_MIN
+ (MissionMonCompComp.SPAWN_X_MAX - MissionMonCompComp.SPAWN_X_MIN)
* (posIndex / Math.max(1, BOSS_COUNT + MINION_COUNT - 1));
const landingY = basePos.y + (monData.isBoss ? 6 : 0);
const landingY = basePos.y;
const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0);
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;

View File

@@ -11,23 +11,20 @@
* 设计层次:
* 1. BossComboLibrary - Boss 组合库(小怪构成 + Boss/小怪技能池 + 抽取权重)
* 2. MonSkillPool/BossSkillPool - 怪物技能池atking / atked / dead 等全触发类型)
* 3. RogueSpawningEngine - 生成引擎(按英雄强度反推怪物强度
* 3. RogueSpawningEngine - 生成引擎(基础属性 × 可调系数
*
* 核心公式:
* heroPower = Σ calcHeroPower(HeroInfo[uuid], lv) (场上存活英雄)
* targetPower = heroPower × BOSS_TYPE_RATIO × FINAL_HP_MUL × MON_POWER_MUL
* hpScale = targetPower ÷ Σ 怪物基础强度
* 怪物 hp = base_hp × hpScale (走总强度框架,自动匹配玩家)
*
* 怪物 ap 独立挂钩玩家输出(不走总强度,避免被 hp 挤占):
* 怪物 ap = 玩家平均攻击 × AP_GROWTH_FINAL × AP_RELATIVE × 类型系数
* 核心公式(基础属性 × 可调系数)
* 小怪 hp = HeroInfo 基础hp × MON_HP_MUL小怪 ap = HeroInfo 基础ap × MON_AP_MUL
* Boss 基础面板固定为初始英雄 2 倍强度BOSS_BASE_HP/BOSS_BASE_AP与小怪共用同一套系数
* 场上 Boss 恰好为小怪 2 倍强度(如需额外压场可调 BOSS_HP_BONUS_MUL
* 小怪基础面板与 1 级英雄持平(近战镜像 hp300/ap22/cd1.8),默认系数校准为
* 整场战斗3 英雄 vs 4 小怪 + 1 Boss双方对砍 ≈ WAVE_DURATION(30s)。
* 手感微调只改 MON_HP_MUL / MON_AP_MUL / BOSS_HP_BONUS_MUL 三个系数即可。
*/
import { HeroInfo, MonType, MonTypeName, calcHeroPower, TriggerGrouped, LvReviveEntry } from "../common/config/heroSet";
import { HeroInfo, MonType, MonTypeName, TriggerGrouped, LvReviveEntry } from "../common/config/heroSet";
import { SkillOverrides } from "../common/config/SkillSet";
import { FacSet } from "../common/config/GameSet";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
// ======================== 1. 战斗常量 ========================
@@ -38,18 +35,20 @@ export const WAVE_DURATION = 30;
export const BOSS_COUNT = 1;
export const MINION_COUNT = 4;
/** Boss 战强度系数(原压力回合系数) */
export const BOSS_TYPE_RATIO = 1.2;
/** 最终 Boss 战 HP 乘区(原 wave 20 配置) */
export const FINAL_HP_MUL = 1.8;
/** 全局怪物强度系数2/3 = 强度削弱三分之一,同时作用于 hp 缩放与 ap 挂钩公式 */
export const MON_POWER_MUL = 2 / 3;
/** 怪物 ap 最终递进系数(原 wave 20 满档 3.0 */
export const AP_GROWTH_FINAL = 3.0;
/** 怪物 ap 相对玩家平均攻击的基础系数(<1 保证单怪不至于一击致命,由 Boss/精英类型再上浮) */
export const AP_RELATIVE = 0.6;
/** Boss 血量加成(原 wave 20 bossBonusHpMul = 1.6 */
export const BOSS_HP_BONUS_MUL = 1.6;
/** 怪物血量可调系数:最终 hp = HeroInfo 基础hp × MON_HP_MUL。
* 2.6 = 校准后怪物总血量 / 3 英雄 30s 输出≈30 DPS双方对砍 ≈ 30s 分胜负 */
export const MON_HP_MUL = 2.6;
/** 怪物攻击可调系数:最终 ap = HeroInfo 基础ap × MON_AP_MUL。
* 0.9 使 4 小怪+1 Boss 的总 DPS ≈ 2030s 恰好磨光 3 英雄总血量≈600 */
export const MON_AP_MUL = 0.9;
/** Boss 基础面板 = 初始英雄基准hp300/ap22× 2 倍强度,
* 不读取 Boss HeroInfo 的差异化高基础值,避免数值膨胀 */
export const BOSS_BASE_HP = 600;
export const BOSS_BASE_AP = 44;
/** Boss 血量额外加成(在 MON_HP_MUL 之上乘算,默认 1.0 表示不再额外膨胀,保留作压场微调旋钮) */
export const BOSS_HP_BONUS_MUL = 1.0;
/** 战斗超时(秒):超过后强制结算整局(残留怪销毁并扣留存分) */
export const BATTLE_TIMEOUT = 90;
@@ -172,7 +171,7 @@ export function pickBossCombo(): BossComboConfig | null {
/**
* 怪物技能套装(一套完整的触发技能配置)
* 覆盖触发时机call / atking / atked / dead / fstart / fend / revive
* 覆盖触发时机call / atking / atked / dead / fstart / revive
*/
export interface MonSkillSet {
id: string;
@@ -189,8 +188,6 @@ export interface MonSkillSet {
dead?: TriggerGrouped;
/** 战斗开始触发 */
fstart?: TriggerGrouped;
/** 战斗结束触发 */
fend?: TriggerGrouped;
/** 复活 */
revive?: LvReviveEntry[];
/** 在技能池中的抽取权重 */
@@ -344,7 +341,6 @@ export interface GeneratedMonster {
atked?: TriggerGrouped;
dead?: TriggerGrouped;
fstart?: TriggerGrouped;
fend?: TriggerGrouped;
revive?: LvReviveEntry[];
};
}
@@ -364,19 +360,18 @@ export const TestModeConfig = {
atked: undefined as TriggerGrouped | undefined,
dead: undefined as TriggerGrouped | undefined,
fstart: undefined as TriggerGrouped | undefined,
fend: undefined as TriggerGrouped | undefined,
};
// ======================== 8. 生成引擎 ========================
/**
* Boss 组合生成引擎
* 随机抽一组 Boss 组合,按英雄强度反推怪物强度(最终 Boss 战档位)
* 随机抽一组 Boss 组合,怪物最终属性 = HeroInfo 基础属性 × 可调系数
*/
export class RogueSpawningEngine {
/**
* 生成一场战斗的怪物列表:随机一组 Boss 组合1 Boss + 组合小怪)。
* 生成一场战斗的怪物列表:随机一组 Boss 组合1 Boss + 4 小怪,属性走基础×系数)。
* Boss 压队尾(登场时由 MissionMonComp 统一布位)。
* @returns 怪物列表小怪在前、Boss 在尾)
*/
@@ -406,21 +401,11 @@ export class RogueSpawningEngine {
monsters.push(this.makeBoss());
}
// 属性缩放:直接取最终 Boss 战档位系数
const heroPower = this.getCurrentHeroPower();
const targetPower = heroPower * BOSS_TYPE_RATIO * MON_POWER_MUL;
const totalBasePower = monsters.reduce((sum, m) => {
const info = HeroInfo[m.uuid];
return sum + (info ? calcHeroPower(info, 1) : m.hp + m.ap);
}, 0);
if (totalBasePower > 0 && targetPower > 0) {
const hpScale = (targetPower * FINAL_HP_MUL) / totalBasePower;
const heroAvgAp = this.getCurrentHeroAttack();
for (const m of monsters) {
m.hp = Math.max(1, Math.round(m.hp * hpScale));
// ap = 玩家平均攻击 × 最终递进 × 基础系数 × 全局强度系数 × 类型系数
m.ap = Math.max(1, Math.round(heroAvgAp * AP_GROWTH_FINAL * AP_RELATIVE * MON_POWER_MUL * this.getApTypeFactor(m)));
}
// 属性缩放:基础属性 × 可调系数Boss 基础面板已是英雄 2 倍BOSS_HP_BONUS_MUL 默认 1.0
// 校准目标Boss 场上强度恰为小怪 2 倍,双方对砍 ≈ 30s
for (const m of monsters) {
m.hp = Math.max(1, Math.round(m.hp * MON_HP_MUL * (m.isBoss ? BOSS_HP_BONUS_MUL : 1)));
m.ap = Math.max(1, Math.round(m.ap * MON_AP_MUL));
}
// 挂载组合技能Boss 专属池 / 小怪普通池)
@@ -443,45 +428,6 @@ export class RogueSpawningEngine {
reset(): void {
}
// ======================== 私有生成子算法 ========================
/**
* 计算场上存活英雄的总强度
* 通过 ECS 查询所有 HeroAttrsComp累加 calcHeroPower
*/
private getCurrentHeroPower(): number {
let total = 0;
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
const attrs = entity.get(HeroAttrsComp);
if (!attrs || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
const info = HeroInfo[attrs.hero_uuid];
if (!info) return;
// 使用英雄当前等级(升级卡驱动),保证强度评估与实战一致
const lv = Math.max(1, attrs.lv || 1);
total += calcHeroPower(info, lv);
});
// 兜底:无英雄时返回基准强度,避免除零
return Math.max(total, 100);
}
/**
* 计算场上存活英雄的输出强度(用于怪物 ap 挂钩玩家,独立于总强度框架)。
* 取玩家英雄的"实时 ap 平均",怪物 ap 以此为锚,保证玩家输出越高、被反击越痛。
*/
private getCurrentHeroAttack(): number {
let totalAp = 0;
let count = 0;
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
const attrs = entity.get(HeroAttrsComp);
if (!attrs || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
totalAp += Math.max(1, attrs.ap || 1);
count++;
});
// 兜底:无英雄时返回基准攻击,避免除零
if (count === 0) return 30;
return totalAp / count; // 平均单英雄攻击
}
/** 测试模式:完全绕过引擎逻辑 */
private generateTestWave(): GeneratedMonster[] {
const count = Math.max(1, TestModeConfig.spawnCount || 1);
@@ -502,14 +448,13 @@ export class RogueSpawningEngine {
atked: TestModeConfig.atked,
dead: TestModeConfig.dead,
fstart: TestModeConfig.fstart,
fend: TestModeConfig.fend,
},
});
}
return monsters;
}
/** 生成 Boss基础血量带最终战加成,属性随后由总强度框架统一缩放) */
/** 生成 Boss基础面板固定为初始英雄 2 倍强度,属性随后由 generateBossCombo 统一系数缩放) */
private makeBoss(): GeneratedMonster {
const isMeleeBoss = Math.random() < 0.5;
const type = isMeleeBoss ? MonType.MeleeBoss : MonType.LongBoss;
@@ -521,35 +466,17 @@ export class RogueSpawningEngine {
}
const uuid = uuids[Math.floor(Math.random() * uuids.length)];
const baseInfo = HeroInfo[uuid];
const baseHp = baseInfo ? baseInfo.hp : 500;
const baseAp = baseInfo ? baseInfo.ap : 30;
return {
uuid,
type,
hp: Math.round(baseHp * BOSS_HP_BONUS_MUL),
ap: baseAp,
hp: BOSS_BASE_HP,
ap: BOSS_BASE_AP,
isBoss: true,
spawnIndex: 0, // 占位值,由 generateBossCombo 统一分配
batch: 0,
};
}
/**
* 怪物 ap 类型系数Boss 上浮,远程/召唤稍降(避免玻璃大炮互秒),近战/重甲基准。
*/
private getApTypeFactor(m: GeneratedMonster): number {
if (m.isBoss) return 2.2; // Boss 单发重,符合"压场"定位
if (m.isBossGuard) return 1.2; // 护卫略高于杂鱼
switch (m.type) {
case MonType.Long: return 0.9;
case MonType.Summoner: return 0.7;
case MonType.Assassin: return 1.1;
default: return 1.0; // Melee / Heavy
}
}
/** 生成普通怪物 */
private makeMonster(type: MonType, spawnIndex: number): GeneratedMonster {
let uuids = MonList[type];
@@ -587,7 +514,6 @@ export class RogueSpawningEngine {
atked: picked.atked,
dead: picked.dead,
fstart: picked.fstart,
fend: picked.fend,
revive: picked.revive,
};
}

View File

@@ -13,7 +13,6 @@
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每回合次数)
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
* - FightEnd (5):监听 FightEnd 事件(每回合结束派发),按 trigger_limit 全局累计上限
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
* - HeroCall (7):监听 MasterCalled主角/技能召唤)+ ReviveSuccess复活
*
@@ -145,7 +144,6 @@ export class SkillBoxComp extends CCComp {
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
oops.message.off(GameEvent.FightStart, this.onFightStart, this);
oops.message.off(GameEvent.FightStart, this.onEventTrigger, this);
oops.message.off(GameEvent.FightEnd, this.onEventTrigger, this);
oops.message.off(GameEvent.HeroDead, this.onEventTrigger, this);
oops.message.off(GameEvent.MasterCalled, this.onEventTrigger, this);
oops.message.off(GameEvent.ReviveSuccess, this.onEventTrigger, this);
@@ -289,7 +287,6 @@ export class SkillBoxComp extends CCComp {
* - Interval: 监听 FightStart进入战斗后由 update 帧驱动计时
* - Field: 不主动施法(实际生效由 FieldSkillSet 处理)
* - FightStart: 监听 FightStart 事件
* - FightEnd: 监听 FightEnd 事件
* - HeroDead: 监听 HeroDead 事件(已在派发处做阵营过滤)
* - HeroCall: 监听 MasterCalled主角/技能召唤)+ ReviveSuccess复活
*
@@ -320,10 +317,6 @@ export class SkillBoxComp extends CCComp {
oops.message.on(GameEvent.FightStart, this.onEventTrigger, this);
break;
case CardTriggerType.FightEnd:
oops.message.on(GameEvent.FightEnd, this.onEventTrigger, this);
break;
case CardTriggerType.HeroDead:
oops.message.on(GameEvent.HeroDead, this.onEventTrigger, this);
break;
@@ -345,7 +338,7 @@ export class SkillBoxComp extends CCComp {
* 事件型触发的统一入口:
* - Instant 类型:按 trigger_times 上限判定,复用 current_trigger_times 跟踪
* (保持与原 is_instant 行为一致,且 NewWave 中也用 current_trigger_times
* - 事件型FightStart/FightEnd/HeroDead/HeroCall按 trigger_limit 上限判定,使用 trigger_count 跟踪
* - 事件型FightStart/HeroDead/HeroCall按 trigger_limit 上限判定,使用 trigger_count 跟踪
*
* 注意:本方法不读取事件 payload仅作触发信号使用避免 MasterCalled 不同 payload 字段引发的兼容问题)。
*/
@@ -441,7 +434,6 @@ export class SkillBoxComp extends CCComp {
const showRemainCount =
this.trigger_type === CardTriggerType.Interval ||
this.trigger_type === CardTriggerType.FightStart ||
this.trigger_type === CardTriggerType.FightEnd ||
this.trigger_type === CardTriggerType.HeroDead ||
this.trigger_type === CardTriggerType.HeroCall;
@@ -450,7 +442,6 @@ export class SkillBoxComp extends CCComp {
// 事件型按 trigger_limitInterval 按 t_times
const isEvent =
this.trigger_type === CardTriggerType.FightStart ||
this.trigger_type === CardTriggerType.FightEnd ||
this.trigger_type === CardTriggerType.HeroDead ||
this.trigger_type === CardTriggerType.HeroCall;
const used = isEvent ? this.trigger_count : this.current_trigger_times;
@@ -520,7 +511,7 @@ export class SkillBoxComp extends CCComp {
* 处理维持回合逻辑:递减剩余回合,或者重置触发次数。
*
* 各类型在新一回合的行为:
* - Instant/Interval/FightStart/FightEnd:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
* - Instant/Interval/FightStart按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
* - Field被动生效跟随 keep_waves 决定存活
* - HeroDead/HeroCall跨回合触发的事件型trigger_count全局不重置仅 keep_waves 控制存活
*/

View File

@@ -74,6 +74,9 @@ export class SMoveDataComp extends ecs.Comp {
/** 是否自动销毁(到达目标后) */
autoDestroy: boolean = true;
/** 直线飞行距离(仅 runType=linear 生效,<=0 时延长终点飞出屏幕) */
distance: number = 0;
reset() {
this.startPos.set(0, 0, 0);
@@ -98,6 +101,7 @@ export class SMoveDataComp extends ecs.Comp {
this.startTime = 0;
this.totalTime = 0;
this.autoDestroy = true;
this.distance = 0;
}
/**

View File

@@ -1,4 +1,4 @@
import { v3, Vec3 ,Node} from "cc";
import { v3, Vec3, Node } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { EType, RType, SkillSet } from "../common/config/SkillSet";
import { SMoveDataComp } from "./SMoveComp";
@@ -26,46 +26,46 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
filter(): ecs.IMatcher {
return ecs.allOf(SMoveDataComp, SkillView);
}
entityEnter(entity: ecs.Entity): void {
const moveComp = entity.get(SMoveDataComp);
const skillView = entity.get(SkillView);
if (!moveComp || !skillView || !skillView.node) return;
// 获取技能配置
const skillConfig = SkillSet[moveComp.s_uuid];
if (!skillConfig) {
mLogger.warn(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能配置不存在: ${moveComp.s_uuid}`);
return;
}
// 根据配置设置移动速度与加速度
if (skillConfig.speed > 0) {
// 移动参数由 prefab 的 SkillView @property 决定speed<=0 时 Skill.load 已回退配置表值)
if (moveComp.speed <= 0) {
moveComp.speed = skillConfig.speed;
}
if (skillConfig.is_accel) {
moveComp.isAccelerate = true;
}
// 根据runType设置初始位置
this.initializePosition(moveComp, skillView);
// 开始移动(除了固定位置类型)
if (moveComp.runType !== RType.fixed && moveComp.runType !== RType.fixedEnd) {
moveComp.startMove();
}
mLogger.log(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能 ${skillConfig.name} 开始移动,类型: ${moveComp.runType}`);
}
/**
* 根据runType初始化技能位置
*/
private initializePosition(moveComp: SMoveDataComp, skillView: SkillView): void {
const node = skillView.node;
switch(moveComp.runType) {
switch (moveComp.runType) {
case RType.linear:
// linear类型根据atk_x和atk_y调整起始位置
const adjustedStartPos = v3(
@@ -73,35 +73,50 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
moveComp.startPos.y + moveComp.atk_y,
moveComp.startPos.z
);
const originTargetPos = v3(
moveComp.targetPos.x,
moveComp.targetPos.y,
moveComp.targetPos.z
);
const direction = new Vec3();
Vec3.subtract(direction, originTargetPos, adjustedStartPos);
// 延长终止点统一消亡点的x坐标为 +-500
const targetX = moveComp.scale > 0 ? 500 : -500;
let targetY = originTargetPos.y;
if (Math.abs(direction.x) > 0.01) {
const slope = direction.y / direction.x;
targetY = adjustedStartPos.y + slope * (targetX - adjustedStartPos.x);
let adjustedTargetPos: Vec3;
if (moveComp.distance > 0) {
// 距离模式:沿起始指向目标的方向飞行固定距离
if (direction.length() < 0.01) {
// 起终点重合时退化为按朝向水平飞行
direction.set(moveComp.scale > 0 ? 1 : -1, 0, 0);
}
direction.normalize();
adjustedTargetPos = v3(
adjustedStartPos.x + direction.x * moveComp.distance,
adjustedStartPos.y + direction.y * moveComp.distance,
originTargetPos.z
);
} else {
// 延长终止点统一消亡点的x坐标为 +-500
const targetX = moveComp.scale > 0 ? 500 : -500;
let targetY = originTargetPos.y;
if (Math.abs(direction.x) > 0.01) {
const slope = direction.y / direction.x;
targetY = adjustedStartPos.y + slope * (targetX - adjustedStartPos.x);
}
adjustedTargetPos = v3(
targetX,
targetY,
originTargetPos.z
);
}
const adjustedTargetPos = v3(
targetX,
targetY,
originTargetPos.z
);
moveComp.startPos.set(adjustedStartPos);
moveComp.targetPos.set(adjustedTargetPos);
node.setPosition(adjustedStartPos);
// 设置旋转角度
const dirForAngle = new Vec3();
Vec3.subtract(dirForAngle, adjustedTargetPos, adjustedStartPos);
@@ -113,12 +128,12 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
node.angle = angle;
}
break;
case RType.fixed:
// 固定起始位置
node.setPosition(moveComp.startPos.x, node.position.y, 0);
break;
case RType.fixedEnd:
// 固定结束位置
node.setPosition(moveComp.targetPos.x > 360 ? 300 : moveComp.targetPos.x, moveComp.targetPos.y, 0);
@@ -154,26 +169,26 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
update(entity: ecs.Entity): void {
if(!smc.mission.play ) return;
if(smc.mission.pause) return
if (!smc.mission.play) return;
if (smc.mission.pause) return
const moveComp = entity.get(SMoveDataComp);
const skillView = entity.get(SkillView);
if (!moveComp || !skillView || !skillView.node) return;
// 更新移动进度
const isMoving = moveComp.updateProgress(this.dt);
if (isMoving) {
// 更新节点位置
skillView.node.setPosition(moveComp.currentPos);
// 处理自动旋转(贝塞尔移动)
if (moveComp.runType === RType.bezier) {
this.updateRotation(skillView.node, moveComp);
}
}
// 检查移动完成
if (moveComp.isCompleted && moveComp.autoDestroy) {
// 根据结束类型决定是否销毁
@@ -185,7 +200,7 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
}
/**
* 更新节点旋转(用于贝塞尔移动)
*/
@@ -193,29 +208,29 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (moveComp.progress < 1) {
// 计算下一帧的位置来确定方向
const nextProgress = Math.min(moveComp.progress + 0.01, 1);
const t = moveComp.isAccelerate ?
(nextProgress * nextProgress * 0.7 + nextProgress * 0.3) :
const t = moveComp.isAccelerate ?
(nextProgress * nextProgress * 0.7 + nextProgress * 0.3) :
nextProgress;
const nextPos = v3(0, 0, 0);
// 计算下一个位置
const oneMinusT = 1 - t;
const oneMinusTSquared = oneMinusT * oneMinusT;
const tSquared = t * t;
const twoOneMinusTt = 2 * oneMinusT * t;
nextPos.x = oneMinusTSquared * moveComp.startPos.x +
twoOneMinusTt * moveComp.controlPoint.x +
tSquared * moveComp.targetPos.x;
nextPos.y = oneMinusTSquared * moveComp.startPos.y +
twoOneMinusTt * moveComp.controlPoint.y +
tSquared * moveComp.targetPos.y;
nextPos.x = oneMinusTSquared * moveComp.startPos.x +
twoOneMinusTt * moveComp.controlPoint.x +
tSquared * moveComp.targetPos.x;
nextPos.y = oneMinusTSquared * moveComp.startPos.y +
twoOneMinusTt * moveComp.controlPoint.y +
tSquared * moveComp.targetPos.y;
// 计算方向角度
const direction = new Vec3();
Vec3.subtract(direction, nextPos, moveComp.currentPos);
if (direction.length() > 0.01) {
let angle = Math.atan2(direction.y, direction.x) * (180 / Math.PI);
if (moveComp.scale < 0) {
@@ -236,15 +251,15 @@ export class SMoveHelper {
*/
static setupMoveForSkill(entity: ecs.Entity, startPos: Vec3, targetPos: Vec3, s_uuid: number): void {
let moveComp = entity.get(SMoveDataComp);
if (!moveComp) {
moveComp = entity.add(SMoveDataComp);
}
moveComp.startPos.set(startPos);
moveComp.targetPos.set(targetPos);
moveComp.s_uuid = s_uuid;
// 从技能配置获取默认参数
const skillConfig = SkillSet[s_uuid];
if (skillConfig) {
@@ -253,7 +268,7 @@ export class SMoveHelper {
if (skillConfig.is_accel) moveComp.isAccelerate = true;
}
}
/**
* 检查技能是否正在移动
*/
@@ -261,7 +276,7 @@ export class SMoveHelper {
const moveComp = entity.get(SMoveDataComp);
return moveComp ? moveComp.isMoving : false;
}
/**
* 获取技能移动统计信息
*/
@@ -269,7 +284,7 @@ export class SMoveHelper {
const moveComp = entity.get(SMoveDataComp);
return moveComp ? moveComp.getMoveStats() : null;
}
/**
* 停止技能移动
*/

View File

@@ -146,7 +146,7 @@ export class Skill extends ecs.Entity {
if (node.isValid) node.destroy();
return;
}
if (config.EType != EType.collision) {
if (SView.endType != EType.collision) {
const collider = node.getComponent(BoxCollider2D);
if (collider) {
collider.enabled = false
@@ -174,21 +174,24 @@ export class Skill extends ecs.Entity {
sMoveCom.targetPos.set(targetPos);
sMoveCom.s_uuid = s_uuid;
sMoveCom.scale = caster.node.scale.x < 0 ? -1 : 1;
sMoveCom.runType = config.RType;
sMoveCom.endType = config.EType;
// runType/endType/speed/time 由 prefab 的 SkillView @property 决定,配置表同名字段仅作兜底
sMoveCom.runType = SView.runType;
sMoveCom.endType = SView.endType;
sMoveCom.speed = SView.speed > 0 ? SView.speed : config.speed;
sMoveCom.bezierStartHeight = config.bezier_start_y ?? sMoveCom.bezierStartHeight;
sMoveCom.bezierMidHeight = config.bezier_mid_y ?? sMoveCom.bezierMidHeight;
sMoveCom.bezierArc = config.bezier_arc ?? sMoveCom.bezierArc;
// 从SkillView获取移动参数位置初始化由SMoveSystem统一处理
sMoveCom.atk_x = SView.atk_x;
sMoveCom.atk_y = SView.atk_y;
sMoveCom.distance = SView.distance;
if (config.EType === EType.timeEnd) {
if (SView.endType === EType.timeEnd) {
let sTimeCom = this.get(StimeDataComp);
if (!sTimeCom) sTimeCom = this.add(StimeDataComp);
sTimeCom.reset();
sTimeCom.s_uuid = s_uuid;
sTimeCom.totalTime = Math.max(1, config.time ?? 0);
sTimeCom.totalTime = Math.max(1, SView.time > 0 ? SView.time : (config.time ?? 0));
sTimeCom.hitInterval = Math.max(0.5, config.hitcd || 0);
} else {
const sTimeCom = this.get(StimeDataComp);

View File

@@ -1,8 +1,8 @@
import { _decorator, Animation, CCInteger, Collider2D, Contact2DType, UITransform, v3, Vec3 } from "cc";
import { _decorator, Animation, CCFloat, CCInteger, Collider2D, Contact2DType, Enum, UITransform, v3, Vec3 } 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 { HeroViewComp } from "../hero/HeroViewComp";
import { DTType, EType, SkillConfig, SkillSet } from "../common/config/SkillSet";
import { DTType, EType, RType, SkillConfig, SkillSet } from "../common/config/SkillSet";
import { SDataCom } from "./SDataCom";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { DamageQueueHelper } from "../hero/DamageQueueComp";
@@ -22,13 +22,33 @@ export class SkillView extends CCComp {
@property({ type: CCInteger })
atk_y: number = 0
/** 运动类型:直线/贝塞尔/固定起点/固定终点(由 prefab 配置,优先级高于 SkillSet */
@property({ type: Enum(RType), tooltip: "技能特效运动类型" })
runType: RType = RType.linear;
/** 消亡条件:动画结束/计时结束/碰撞次数结束(由 prefab 配置,优先级高于 SkillSet */
@property({ type: Enum(EType), tooltip: "技能特效消亡条件" })
endType: EType = EType.collision;
/** 移动速度(由 prefab 配置,<=0 时回退 SkillSet.speed */
@property({ type: CCFloat, slide: true, range: [0, 2000, 10], tooltip: "技能特效移动速度" })
speed: number = 0;
/** timeEnd 持续时间(秒),仅 endType=timeEnd 时生效(由 prefab 配置,<=0 时回退 SkillSet.time */
@property({ type: CCFloat, slide: true, range: [0, 30, 0.1], tooltip: "timeEnd 持续时间(秒)" })
time: number = 0;
/** 直线飞行距离(像素),仅 runType=linear 时生效(由 prefab 配置,<=0 时保持默认飞出屏幕的行为) */
@property({ type: CCFloat, slide: true, range: [0, 2000, 10], tooltip: "直线飞行距离(仅 linear 生效)" })
distance: number = 0;
private debugMode: boolean = false;
anim:Animation=null;
group:number=0;
SConf:SkillConfig=null;
sData:SDataCom=null;
s_uuid:number=1001
anim: Animation = null;
group: number = 0;
SConf: SkillConfig = null;
sData: SDataCom = null;
s_uuid: number = 1001
private collider: Collider2D = null; // 缓存碰撞体引用
private pendingDisableCollider: boolean = false;
private isDisposing: boolean = false;
@@ -41,25 +61,25 @@ export class SkillView extends CCComp {
this.pendingDisableCollider = false;
this.isDisposing = false;
this.collider = this.getComponent(Collider2D);
if(this.collider) {
if (this.collider) {
this.collider.group = this.group;
this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
this.collider.enabled = this.SConf?.EType === EType.collision;
this.collider.enabled = this.endType === EType.collision;
}
if(this.node.getComponent(Animation)){
if (this.node.getComponent(Animation)) {
let anim = this.node.getComponent(Animation);
mLogger.log(this.debugMode, 'SkillView', "[SkillCom]:has anim",anim)
mLogger.log(this.debugMode, 'SkillView', "[SkillCom]:has anim", anim)
anim.off(Animation.EventType.FINISHED, this.onAnimationFinished, this);
anim.on(Animation.EventType.FINISHED, this.onAnimationFinished, this);
// 对象池复用时,需要手动播放默认动画(因为 Play On Load 只在首次生效)
if (anim.defaultClip) {
anim.play(anim.defaultClip.name);
}
}
}
onBeginContact (seCol: Collider2D, oCol: Collider2D) {
onBeginContact(seCol: Collider2D, oCol: Collider2D) {
if (!this.sData || !this.SConf) {
mLogger.warn(this.debugMode, 'SkillView', '[SkillView] onBeginContact 缺少 sData 或 SConf忽略此次碰撞');
return;
@@ -126,17 +146,23 @@ export class SkillView extends CCComp {
this.apply_damage(targetView, this.sData.hit_count)
}
onAnimationFinished(){
// animationEnd 类型:动画结束后再关闭碰撞并销毁
if(this.SConf.EType==EType.animationEnd){
onAnimationFinished() {
// animationEnd 类型:动画结束后再关闭碰撞并销毁
if (this.endType == EType.animationEnd) {
this.disable_collider_now();
this.ent.destroy()
}
return;
}
// fixed 类型不移动,无法通过移动完成销毁;动画播完作为兜底销毁时机
if (this.runType == RType.fixed) {
this.disable_collider_now();
this.ent.destroy()
}
}
// 动画帧事件 atk仅负责开启一帧碰撞窗口不负责伤害与计数
public atk(args:any){
if(!this.SConf) return;
if(this.SConf.EType==EType.collision) return
public atk(args: any) {
if (!this.SConf) return;
if (this.endType == EType.collision) return
if (this.enable_collider_safely()) {
mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf?.name}] 开启碰撞检测`);
this.scheduleOnce(() => {
@@ -179,7 +205,7 @@ export class SkillView extends CCComp {
finalRatio,
);
}
close_collider(){
close_collider() {
if (!this.collider) return;
if (this.pendingDisableCollider && !this.collider.enabled) return;
this.pendingDisableCollider = true;

View File

@@ -19,8 +19,6 @@ export class TooltipCom extends CCComp {
@property(Node)
fstart_bg: Node = null;
@property(Node)
fend_bg: Node = null;
@property(Node)
dead_bg: Node = null;
@property(Node)
revive_bg: Node = null;
@@ -146,10 +144,10 @@ export class TooltipCom extends CCComp {
this.node.setPosition(v3(this.node.position.x, currentY - 60));
this.node.setSiblingIndex(topSiblingIndex);
break;
case TooltipTypes.shield: // 护盾格挡
case TooltipTypes.shield: // 护盾减免
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
this.node.setSiblingIndex(topSiblingIndex);
this.setupLabel("add_life", "hp", "格挡");
this.setupLabel("add_life", "hp", "减免");
isHeal = true;
break;
}
@@ -176,7 +174,6 @@ export class TooltipCom extends CCComp {
this.atking_bg && (this.atking_bg.active = false);
this.atked_bg && (this.atked_bg.active = false);
this.fstart_bg && (this.fstart_bg.active = false);
this.fend_bg && (this.fend_bg.active = false);
this.dead_bg && (this.dead_bg.active = false);
this.revive_bg && (this.revive_bg.active = false);
@@ -186,7 +183,6 @@ export class TooltipCom extends CCComp {
[SkillTriggerType.Atking]: this.atking_bg,
[SkillTriggerType.Atked]: this.atked_bg,
[SkillTriggerType.FStart]: this.fstart_bg,
[SkillTriggerType.FEnd]: this.fend_bg,
[SkillTriggerType.Dead]: this.dead_bg,
[SkillTriggerType.Revive]: this.revive_bg,
};

View File

@@ -0,0 +1,83 @@
/**
* 一次性迁移脚本:把 SkillSet 中的 RType/EType/speed/time 刷入各技能 prefab 的 SkillView 序列化字段。
* 用法: node tools/sync-skill-prefab.js
* 注意: 直接改写 prefab JSON运行前请保证工作区已提交。
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const SKILL_SET = path.join(ROOT, 'assets/script/game/common/config/SkillSet.ts');
const PREFAB_DIR = path.join(ROOT, 'assets/resources/game/skill/atk');
// SkillView 组件在 prefab 中的脚本类 cid
const SKILL_VIEW_CID = '57aabs7TE1J5obTAZczc+64';
const RTYPE = { linear: 0, bezier: 1, fixed: 2, fixedEnd: 3 };
const ETYPE = { animationEnd: 0, timeEnd: 1, collision: 2 };
/** 从 SkillSet.ts 解析 sp_name -> {runType, endType, speed, time} */
function parseSkillSet(src) {
const map = new Map();
// 匹配每个技能条目块
const entryRe = /\d+:\s*\{([\s\S]*?)\n\s*\},/g;
let m;
while ((m = entryRe.exec(src)) !== null) {
const body = m[1];
const sp = body.match(/sp_name:\s*"([^"]+)"/);
if (!sp) continue;
const spName = sp[1];
const rt = body.match(/RType:\s*RType\.(\w+)/);
const et = body.match(/EType:\s*EType\.(\w+)/);
const speed = body.match(/speed:\s*([\d.]+)/);
const time = body.match(/\btime:\s*([\d.]+)/);
const entry = {
runType: rt ? RTYPE[rt[1]] : undefined,
endType: et ? ETYPE[et[1]] : undefined,
speed: speed ? Number(speed[1]) : undefined,
time: time ? Number(time[1]) : 0,
};
// 多个技能可共用同一 sp_name保留首个非 undefined 组合
if (!map.has(spName)) map.set(spName, entry);
}
return map;
}
function main() {
const src = fs.readFileSync(SKILL_SET, 'utf8');
const confMap = parseSkillSet(src);
const files = fs.readdirSync(PREFAB_DIR).filter(f => f.endsWith('.prefab'));
const report = { updated: [], skipped: [], noConf: [] };
for (const file of files) {
const spName = path.basename(file, '.prefab');
const conf = confMap.get(spName);
const fp = path.join(PREFAB_DIR, file);
const json = JSON.parse(fs.readFileSync(fp, 'utf8'));
// 定位 SkillView 组件
const comp = json.find(o => o && o.__type__ === SKILL_VIEW_CID);
if (!comp) { report.skipped.push(file + ' (no SkillView)'); continue; }
if (!conf) { report.noConf.push(file); continue; }
if (conf.runType !== undefined) comp.runType = conf.runType;
if (conf.endType !== undefined) comp.endType = conf.endType;
if (conf.speed !== undefined) comp.speed = conf.speed;
comp.time = conf.time ?? 0;
fs.writeFileSync(fp, JSON.stringify(json, null, 2) + '\n', 'utf8');
report.updated.push(`${file} -> runType=${comp.runType} endType=${comp.endType} speed=${comp.speed} time=${comp.time}`);
}
console.log('=== updated ===');
report.updated.forEach(s => console.log(' ' + s));
console.log('=== no config matched ===');
report.noConf.forEach(s => console.log(' ' + s));
console.log('=== skipped ===');
report.skipped.forEach(s => console.log(' ' + s));
}
main();