6 Commits

Author SHA1 Message Date
panFD
a5ca43a62c refactor: 统一全量buff数值为百分比口径
本次变更对齐了所有buff和技能的数值结算规则:
1.  新增项目规范明确buff数值统一使用百分比/百分点结算
2.  修改技能描述配置、属性计算逻辑,将固定数值加成改为百分比计算
3.  调整buff配置的修改类型为PercentAdd,同步更新buff说明文本
4.  重构属性计算逻辑,区分百分比加成和固定加成的处理方式
5.  更新战力计算逻辑适配新的百分比口径规则
2026-08-16 19:21:23 +08:00
panFD
284d124486 feat(skill): 新增召唤技能配置与错峰发射逻辑
1.  为技能prefab新增summon_count、forceStart坐标与height_offset配置项
2.  新增全局SUMMON_INTERVAL配置用于控制召唤物发射间隔
3.  重构施法系统,支持按配置召唤多个技能实体并错峰执行
4.  为多发/召唤技能添加纵向偏移档位,优化弹道分布
5.  屏蔽召唤技能的风怒额外触发逻辑,修复普攻链计入问题
2026-08-16 18:56:15 +08:00
panFD
8a506d9b83 feat(skill): 新增并调整技能配置
1. 为range3技能prefab新增forceStartX和forceStartY参数
2. 调整6102-6106技能的act动作字段
3. 新增6107万剑朝宗和6108开山斧两个技能
4. 更新6201陨石术的act动作为max1
2026-08-16 17:40:12 +08:00
panFD
6f988dabed feat(skill): add force spawn position setting for skills
add forceStartX and forceStartY properties to control skill spawn position
directly use world coordinates when the values are >=0, otherwise use original offset logic
2026-08-16 16:49:12 +08:00
panFD
4fc9acad7c feat(skill): add 4 new projectile skill configs and assets
新增了暗影术、光波、能量波、毒气弹四个远程技能配置,同时添加了对应的range2、range3攻击预制件与动画元文件,补充完整技能资源与配置表。
2026-08-16 16:40:03 +08:00
panFD
a601b97f75 chore: 移除金币技能相关代码和配置
删除了SkillKind.Gold枚举、SkillConfig中的gold字段,移除了6303号金币技能配置,同时删除了施法系统中对应的金币添加逻辑
2026-08-15 23:48:47 +08:00
19 changed files with 9611 additions and 110 deletions

View File

@@ -67,6 +67,7 @@ alwaysApply: true
1. **技能底座纯净化**:SkillSet 中的技能只定义基础行为(伤害、弹道、命中),**禁止**在基础配置中写死 `timed_buff_id` / `buff_type` 等效果字段
2. **效果由引用处注入**:英雄/怪物引用技能时,通过 `HSkillInfo.overrides` 按需附加 debuff/buff,实现"同一技能、不同效果"
3. **数值覆盖优先**:`ap`(伤害百分比)、`hit_count`、`crt` 等数值差异,统一由 `overrides` 覆写,而非新建相似技能
4. **Buff 统一百分比口径**:无论永久 buff(`buff_type`)还是计时 buff(`timed_buff_id`),修饰数值均按百分比/百分点结算;`ap`/`hp_max` 按基础值相对放大,概率类属性按百分点累加
## SkillSet 配置规范

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"ver": "2.0.3",
"importer": "animation-clip",
"imported": true,
"uuid": "cb225ade-8b82-4bd0-913c-1f4b9cf8690a",
"files": [
".cconb"
],
"subMetas": {},
"userData": {
"name": "range2"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"ver": "2.0.3",
"importer": "animation-clip",
"imported": true,
"uuid": "9e3cd340-acbc-409e-9ceb-ca4678ecaea4",
"files": [
".cconb"
],
"subMetas": {},
"userData": {
"name": "range3"
}
}

View File

@@ -258,6 +258,9 @@
"speed": 720,
"time": 0,
"distance": 0,
"forceStartX": -1,
"forceStartY": -1,
"summon_count": 3,
"_id": ""
},
{

View File

@@ -0,0 +1,928 @@
[
{
"__type__": "cc.Prefab",
"_name": "range2",
"_objFlags": 0,
"__editorExtras__": {},
"_native": "",
"data": {
"__id__": 1
},
"optimizationPolicy": 0,
"persistent": false
},
{
"__type__": "cc.Node",
"_name": "range2",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": null,
"_children": [
{
"__id__": 2
}
],
"_active": true,
"_components": [
{
"__id__": 30
},
{
"__id__": 32
},
{
"__id__": 34
},
{
"__id__": 36
},
{
"__id__": 38
},
{
"__id__": 40
}
],
"_prefab": {
"__id__": 42
},
"_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-001",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 1
},
"_children": [
{
"__id__": 3
},
{
"__id__": 21
}
],
"_active": true,
"_components": [
{
"__id__": 27
}
],
"_prefab": {
"__id__": 29
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -111.631,
"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__": 2
},
"_children": [
{
"__id__": 4
},
{
"__id__": 10
}
],
"_active": true,
"_components": [
{
"__id__": 16
},
{
"__id__": 18
}
],
"_prefab": {
"__id__": 20
},
"_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": "f",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 3
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 5
},
{
"__id__": 7
}
],
"_prefab": {
"__id__": 9
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 24.727,
"y": 20.405,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0.3826834323650898,
"w": 0.9238795325112867
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 45
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 4
},
"_enabled": true,
"__prefab": {
"__id__": 6
},
"_contentSize": {
"__type__": "cc.Size",
"width": 104,
"height": 92
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "10vlMhQY5AqYpItsX+36u6"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 4
},
"_enabled": true,
"__prefab": {
"__id__": 8
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": {
"__uuid__": "abc35b6e-b052-4835-a3a3-cb85abd47f4d@bb1c9",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "86HwoxWmhBnL8DBTzKTBFq"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "4eVIHUruVEPKeL4meyjIez",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "f-001",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 3
},
"_children": [],
"_active": false,
"_components": [
{
"__id__": 11
},
{
"__id__": 13
}
],
"_prefab": {
"__id__": 15
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 24.727,
"y": 20.405,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0.3826834323650898,
"w": 0.9238795325112867
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1,
"y": 1,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 45
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 10
},
"_enabled": true,
"__prefab": {
"__id__": 12
},
"_contentSize": {
"__type__": "cc.Size",
"width": 104,
"height": 92
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "7cUVEvKCxOR5PL3eKLw+/m"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 10
},
"_enabled": true,
"__prefab": {
"__id__": 14
},
"_customMaterial": {
"__uuid__": "8eee8ab1-fe48-4b22-b956-3f5c18fc4810",
"__expectedType__": "cc.Material"
},
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": {
"__uuid__": "abc35b6e-b052-4835-a3a3-cb85abd47f4d@bb1c9",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "efxCF2oilFR4Mdk1TmeurF"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "a1r6GcGwpNTrM8WvvrEqLU",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 17
},
"_contentSize": {
"__type__": "cc.Size",
"width": 100,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "70DHEflmNKcrXz52R+PxIm"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 19
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": null,
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "717jT1RqhARpnrK9e9y6r8"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "aevRQtaVFHroFdWuCtBPIE",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "zha_00052",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 2
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 22
},
{
"__id__": 24
}
],
"_prefab": {
"__id__": 26
},
"_lpos": {
"__type__": "cc.Vec3",
"x": 132.79399999999998,
"y": -95,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1.7,
"y": 1.5,
"z": 1
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 21
},
"_enabled": true,
"__prefab": {
"__id__": 23
},
"_contentSize": {
"__type__": "cc.Size",
"width": 200,
"height": 180
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "20/MgTXdpHnqgNPTUDwW0f"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 21
},
"_enabled": true,
"__prefab": {
"__id__": 25
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 0
},
"_spriteFrame": {
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@966d7",
"__expectedType__": "cc.SpriteFrame"
},
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "f1ZaUa+eJBG6O5sGM6aJ3f"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "b5OptiT15EzZkBo/RRwPJ7",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 2
},
"_enabled": true,
"__prefab": {
"__id__": 28
},
"_contentSize": {
"__type__": "cc.Size",
"width": 100,
"height": 100
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.5,
"y": 0.5
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "f9OwrGiONLvrECWq4QjFuy"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "ccwYlWWrFMBbLxMlGmAjxg",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 31
},
"_contentSize": {
"__type__": "cc.Size",
"width": 150,
"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__": 33
},
"atk_x": 20,
"atk_y": 40,
"runType": 2,
"endType": 0,
"speed": 720,
"time": 0,
"distance": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
},
{
"__type__": "cc.RigidBody2D",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 35
},
"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__": 37
},
"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": 150,
"height": 120
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
},
{
"__type__": "cc.Animation",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 39
},
"playOnLoad": true,
"_clips": [
{
"__uuid__": "cb225ade-8b82-4bd0-913c-1f4b9cf8690a",
"__expectedType__": "cc.AnimationClip"
}
],
"_defaultClip": {
"__uuid__": "cb225ade-8b82-4bd0-913c-1f4b9cf8690a",
"__expectedType__": "cc.AnimationClip"
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "20U0hP/NZErJJXRCEib1lq"
},
{
"__type__": "cc.Sprite",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 41
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": null,
"_type": 0,
"_fillType": 0,
"_sizeMode": 1,
"_fillCenter": {
"__type__": "cc.Vec2",
"x": 0,
"y": 0
},
"_fillStart": 0,
"_fillRange": 0,
"_isTrimmedMode": true,
"_useGrayscale": false,
"_atlas": null,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "bdU1jaclVIPaDk4N8vuMfY"
},
{
"__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": "995d12bf-b91c-414d-b815-a0a1cc62ee5e",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "range2"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "904a5d1f-6888-4b8c-a2ad-8a6db80db202",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "range3"
}
}

View File

@@ -88,8 +88,8 @@ export const BuffList: Record<number, BuffConfig> = {
7001: {
id: 7001, name: "攻击强化", icon: "Stat_Attack_03",
category: BuffCategory.Buff, duration: 10, max_stack: 0,
modifiers: [{ attr: Attrs.ap, op: ModOp.Flat, value: 5 }],
info: "攻击力 +5,持续 10 秒",
modifiers: [{ attr: Attrs.ap, op: ModOp.PercentAdd, value: 5 }],
info: "攻击力提升 5%,持续 10 秒",
},
// 易伤(灼热火球附加;受到伤害时附加固定值,可被易伤抗性百分比减免)
// 注:modifiers.attr 用 hp 仅为占位以通过 aggregateTimedMods 结构校验,实际易伤值由 HeroAtkSystem 按 VULNERABLE_BUFF_ID 直接汇总
@@ -124,8 +124,8 @@ export const BuffList: Record<number, BuffConfig> = {
7006: {
id: 7006, name: "减攻", icon: "Stat_Attack_01",
category: BuffCategory.Debuff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.ap, op: ModOp.Flat, value: -10 }],
info: "攻击力降低 10 点,持续 5 秒",
modifiers: [{ attr: Attrs.ap, op: ModOp.PercentAdd, value: -10 }],
info: "攻击力降低 10%,持续 5 秒",
},
// 出血(每层使目标被暴击率 +1%,微量伤害 DoT;暴击率加成由 HeroAtkSystem 按 BLEED_BUFF_ID 层数结算)
7007: {
@@ -134,12 +134,12 @@ export const BuffList: Record<number, BuffConfig> = {
tick: { interval: 0.5, damage_or_heal: -1, scale_by_ap_pct: 0 },
info: "微量出血,每层使自身被暴击率提升 1%,可叠加,持续 5 秒",
},
// 占卜师战前攻击强化(基础档:+30/5s;高阶通过 buff_value/buff_duration 覆写成长)
// 占卜师战前攻击强化(基础档:+30%/5s;高阶通过 buff_value/buff_duration 覆写成长)
7111: {
id: 7111, name: "战前强攻", icon: "Stat_Attack_03",
category: BuffCategory.Buff, duration: 5, max_stack: 0,
modifiers: [{ attr: Attrs.ap, op: ModOp.Flat, value: 30 }],
info: "攻击力 +30,持续 5 秒",
modifiers: [{ attr: Attrs.ap, op: ModOp.PercentAdd, value: 30 }],
info: "攻击力提升 30%,持续 5 秒",
},
// ==================== 玩家计时性强化(药水/技能用,默认 5s) ====================
@@ -161,77 +161,77 @@ export const BuffList: Record<number, BuffConfig> = {
7012: {
id: 7012, name: "暴击爆发", icon: "Stat_CriticalChance_02",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.critical, op: ModOp.Flat, value: 30 }],
modifiers: [{ attr: Attrs.critical, op: ModOp.PercentAdd, 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 秒",
modifiers: [{ attr: Attrs.knockback_chance, op: ModOp.PercentAdd, value: 30 }],
info: "击退距离提升 30%,持续 5 秒",
},
// 穿透强化(限时)
7014: {
id: 7014, name: "穿透爆发", icon: "Stat_Tripleshot",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.puncture_chance, op: ModOp.Flat, value: 30 }],
modifiers: [{ attr: Attrs.puncture_chance, op: ModOp.PercentAdd, value: 30 }],
info: "穿透概率提升 30%,持续 5 秒",
},
// 攻速强化(限时,走 getEffectiveSkillCd 计时修饰)
7015: {
id: 7015, name: "攻速爆发", icon: "Stat_AttackSpeed_02",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.speed, op: ModOp.Flat, value: 40 }],
modifiers: [{ attr: Attrs.speed, op: ModOp.PercentAdd, value: 40 }],
info: "攻击速度提升 40%,持续 5 秒",
},
// 风怒强化(限时,按概率百分点)
7016: {
id: 7016, name: "风怒爆发", icon: "Stat_CriticalComboChance",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.wfuny, op: ModOp.Flat, value: 20 }],
modifiers: [{ attr: Attrs.wfuny, op: ModOp.PercentAdd, value: 20 }],
info: "风怒概率提升 20%,持续 5 秒",
},
// 击晕概率强化(限时)
7017: {
id: 7017, name: "击晕爆发", icon: "Stat_Stun_01",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.stun_chance, op: ModOp.Flat, value: 30 }],
modifiers: [{ attr: Attrs.stun_chance, op: ModOp.PercentAdd, value: 30 }],
info: "击晕概率提升 30%,持续 5 秒",
},
// 击晕抗性强化(限时)
7018: {
id: 7018, name: "击晕抗性", icon: "Stat_Armor_01",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.stun_res, op: ModOp.Flat, value: 50 }],
modifiers: [{ attr: Attrs.stun_res, op: ModOp.PercentAdd, value: 50 }],
info: "击晕抗性提升 50%,持续 5 秒",
},
// 冰冻抗性强化(限时)
7019: {
id: 7019, name: "冰冻抗性", icon: "Stat_Freeze",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.freeze_res, op: ModOp.Flat, value: 50 }],
modifiers: [{ attr: Attrs.freeze_res, op: ModOp.PercentAdd, value: 50 }],
info: "冰冻抗性提升 50%,持续 5 秒",
},
// 暴伤强化(限时)
7020: {
id: 7020, name: "暴伤爆发", icon: "Stat_Critical_01",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.critical_damage, op: ModOp.Flat, value: 30 }],
modifiers: [{ attr: Attrs.critical_damage, op: ModOp.PercentAdd, value: 30 }],
info: "暴击伤害提升 30%,持续 5 秒",
},
// 冰冻概率强化(限时)
7021: {
id: 7021, name: "冰冻概率爆发", icon: "Stat_Freeze",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.freeze_chance, op: ModOp.Flat, value: 5 }],
modifiers: [{ attr: Attrs.freeze_chance, op: ModOp.PercentAdd, value: 5 }],
info: "冰冻概率提升 5%,持续 5 秒",
},
// 穿透伤害强化(限时)
7022: {
id: 7022, name: "穿透伤害爆发", icon: "Stat_Tripleshot",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.puncture_dmg_bonus, op: ModOp.Flat, value: 30 }],
modifiers: [{ attr: Attrs.puncture_dmg_bonus, op: ModOp.PercentAdd, value: 30 }],
info: "穿透后伤害提升 30%,持续 5 秒",
},
};

View File

@@ -42,6 +42,8 @@ export enum FightSet {
FROST_TIME = 3,//冰冻时间
STUN_TIME = 2,//击晕时间
SKILL_CAST_DELAY = 0.15,
/** 多发/召唤技能每次实例化之间的间隔(秒),首个立即执行 */
SUMMON_INTERVAL = 0.12,
CSKILL_START_X = -340,
CSKILL_START_Y = 30,
/** 护盾数值上限(数值减免型:1 点护盾吸收 1 点伤害) */

View File

@@ -57,16 +57,16 @@ export const SkillDescSet: Record<number, SkillDescConfig> = {
verb: "", unit: "", permField: "ap",
templPerm: "{target}回复攻击力{value}%生命",
},
// 6401 攻击强化:永久 ap=攻击点;计时 buff_value=攻击点
// 6401 攻击强化:永久 ap=攻击%;计时 buff_value=攻击%
6401: {
verb: "攻击力", unit: "", permField: "ap", timedField: "buff_value",
templPerm: "{target}{verb}永久+{value}",
templTimed: "{target}{verb}+{value}, 持续{dur}秒",
verb: "攻击力", unit: "%", permField: "ap", timedField: "buff_value",
templPerm: "{target}{verb}永久+{value}{unit}",
templTimed: "{target}{verb}+{value}{unit}, 持续{dur}秒",
},
// 6402 生命强化:ap = 最大生命点
// 6402 生命强化:ap = 最大生命%
6402: {
verb: "最大生命", unit: "", permField: "ap",
templPerm: "{target}{verb}永久+{value}",
verb: "最大生命", unit: "%", permField: "ap",
templPerm: "{target}{verb}永久+{value}{unit}",
},
// 6403 暴击强化:永久 ap=暴击%;计时 buff_value=暴击%
6403: {

View File

@@ -26,7 +26,6 @@ export enum SkillKind {
Heal = 1,
Shield = 2,
Support = 3,
Gold = 4
}
@@ -131,7 +130,6 @@ export interface SkillConfig {
act: string, // 角色执行的动画
DTType: DTType, // 伤害类型(单体/范围)
ap: number, // 伤害/治疗技能为攻击百分比,护盾技能为免疫次数
gold?: number, // 获取金币技能的金币值
hit_count: number, // 可命中次数
hitcd: number, // 持续伤害的伤害间隔(秒)
speed: number, // 移动速度
@@ -165,7 +163,6 @@ export interface SkillConfig {
export interface SkillOverrides {
TGroup?: TGroup;
ap?: number;
gold?: number;
hit_count?: number;
hitcd?: number;
crt?: number;
@@ -278,40 +275,74 @@ export const SkillSet: Record<number, SkillConfig> = {
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
// 暗影术 — 纯伤害底座
6015: {
uuid: 6015, name: "暗影术", sp_name: "fb-2", icon: "Stat_DarkDamage", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
// 光波 — 纯伤害底座
6016: {
uuid: 6016, name: "光波", sp_name: "fb-3", icon: "Stat_HolyDamage", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
// 能量波 — 纯伤害底座
6017: {
uuid: 6017, name: "能量波", sp_name: "fb-4", icon: "Stat_SplashDamage", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
// 毒气弹 — 纯伤害底座
6018: {
uuid: 6018, name: "毒气弹", sp_name: "fb-5", icon: "Stat_NatureDamage", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
},
//大招
6102: {
uuid: 6102, name: "飓风", sp_name: "fb-2", icon: "Stat_Mana", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
uuid: 6102, name: "飓风", sp_name: "bm-1", icon: "Stat_Mana", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max1",
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害, 高阶技能",
},
6103: {
uuid: 6103, name: "光闪", sp_name: "atk3", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
uuid: 6103, name: "光闪", sp_name: "atk3", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max0",
DTType: DTType.single, ap: 150, hit_count: 5, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
RType: RType.bezier, EType: EType.collision, info: "对周围的敌人造成攻击力150%的伤害",//英雄配置技能的时候,可以叠加出血debuff
},
6104: {
uuid: 6104, name: "气斩", sp_name: "atk2", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
uuid: 6104, name: "气斩", sp_name: "atk2", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max0",
DTType: DTType.single, ap: 150, hit_count: 5, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
RType: RType.bezier, EType: EType.collision, info: "对周围的敌人造成攻击力150%的伤害",//英雄配置技能的时候,可以叠加击退
},
6105: {
uuid: 6105, name: "火焰斩", sp_name: "atk5", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
uuid: 6105, name: "火焰斩", sp_name: "atk5", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max0",
DTType: DTType.single, ap: 150, hit_count: 5, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
RType: RType.bezier, EType: EType.collision, info: "对周围的敌人造成攻击力150%的伤害",//英雄配置技能的时候,可以叠加暴击debuff
},
6106: {
uuid: 6106, name: "闪电斩", sp_name: "atk5", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
uuid: 6106, name: "闪电斩", sp_name: "atk5", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "max0",
DTType: DTType.single, ap: 150, hit_count: 5, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
RType: RType.bezier, EType: EType.collision, info: "对周围的敌人造成攻击力150%的伤害",//英雄配置技能的时候,可以疲惫触发6013闪电术
},
6107: {
uuid: 6201, name: "万剑朝宗", sp_name: "range3", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max1",
DTType: DTType.aoe_grid, stun: 0, ap: 150, hit_count: 6, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.fixed, EType: EType.animationEnd, info: "召唤6把冰剑攻击敌人",//可以附加冰冻增强
},
6108: {
uuid: 6201, name: "开山斧", sp_name: "range2", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max0",
DTType: DTType.aoe_grid, stun: 0, ap: 300, hit_count: 6, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.fixed, EType: EType.animationEnd, info: "召唤巨斧劈向前方敌人",//可以附加击晕增强
},
6201: {
uuid: 6201, name: "陨石术", sp_name: "fb-3", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max",
uuid: 6201, name: "陨石术", sp_name: "fb-3", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max1",
DTType: DTType.aoe_grid, stun: 0, ap: 150, hit_count: 6, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.fixed, EType: EType.animationEnd, info: "召唤陨石范围攻击敌人, 有概率击晕",
},
//============================= ====== 辅助技能 技能卡牌 1 回合技能 ====== ==========================
//============================= ====== 辅助技能 回合技能 ====== ==========================
6301: {
uuid: 6301, name: "护盾", sp_name: "buff_wind", icon: "Stat_Defense", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Shield, ap: 3, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
@@ -322,22 +353,16 @@ export const SkillSet: Record<number, SkillConfig> = {
DTType: DTType.single, kind: SkillKind.Heal, ap: 300, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
RType: RType.fixed, EType: EType.animationEnd, info: "治疗伙伴/自己",
},
//==========================buff 技能 也是 技能卡牌 5 回合技能
6303: {
uuid: 6303, name: "获取金币", sp_name: "buff_wind", icon: "Stat_GoldGainIncrease_01", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "gold", act: "atk",
DTType: DTType.single, kind: SkillKind.Gold, ap: 0, gold: 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, info: "增加一定数量的金币",
},
//==========================buff 技能=====================
6401: {
uuid: 6401, name: "攻击强化", sp_name: "buff_wind", icon: "Stat_Attack_03", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 5, 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.ap, info: "全体友方攻击力提升5点, 持续1次",
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.ap, info: "全体友方攻击力提升5%, 持续1次",
},
6402: {
uuid: 6402, name: "生命强化", sp_name: "buff_wind", icon: "Stat_Hp_02", TGroup: TGroup.Team, readyAnm: "up_hp", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 20, 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.hp_max, info: "全体友方最大生命值提升20点, 持续1次",
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.hp_max, info: "全体友方最大生命值提升20%, 持续1次",
},
6403: {
uuid: 6403, name: "暴击强化", sp_name: "buff_wind", icon: "Stat_CriticalChance_02", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
@@ -382,7 +407,7 @@ export const SkillSet: Record<number, SkillConfig> = {
6411: {
uuid: 6411, name: "穿透伤害强化", sp_name: "buff_wind", icon: "Stat_Tripleshot", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 10, 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.puncture_dmg_bonus, info: "全体友方穿透后伤害提升10%(百分点), 永久生效",
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.puncture_dmg_bonus, info: "全体友方穿透后伤害提升10%, 永久生效",
},
6501: {
uuid: 6501, name: "复活", sp_name: "buff_wind", icon: "Stat_HolyDamage", TGroup: TGroup.Self, readyAnm: "up_ap", endAnm: "", act: "atk",
@@ -391,7 +416,7 @@ export const SkillSet: Record<number, SkillConfig> = {
},
// ==================== 计时性强化技能(药水底座,统一 5s,走 timed_buff_id) ====================
// 注:ap 字段在此类技能中无意义,修饰数值由 BuffList 配置;hit_count=99 确保覆盖全队。
// 注:ap 字段在此类技能中无意义,修饰数值由 BuffList 配置(PercentAdd 百分比口径);hit_count=99 确保覆盖全队。
6502: {
uuid: 6502, name: "计时回血", sp_name: "buff_wind", icon: "Stat_PotionBoost", TGroup: TGroup.Team, readyAnm: "up_green", 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,
@@ -410,7 +435,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",

View File

@@ -1114,7 +1114,7 @@ function calcBuffPermanentEquivalent(buff: BuffConfig, apEff: number, cd: number
}
/** 计算 Support 类技能单次触发的效果价值 */
function calcSupportEffectValue(config: SkillConfig, apEff: number, cd: number, seconds: number): number {
function calcSupportEffectValue(config: SkillConfig, hpEff: number, apEff: number, cd: number, seconds: number): number {
const weights = HERO_POWER_WEIGHTS;
const value = config.ap || 0;
@@ -1131,10 +1131,12 @@ function calcSupportEffectValue(config: SkillConfig, apEff: number, cd: number,
switch (config.buff_type) {
case Attrs.ap:
return (value / cd) * weights.W_DPS_PER_SEC * seconds;
// 永久 buff 已改为百分比口径:value 是百分比,需先换算为基础 ap 的绝对增量
return ((value / 100) * apEff / cd) * weights.W_DPS_PER_SEC * seconds;
case Attrs.hp_max:
case Attrs.hp:
return value * weights.W_HP;
// 永久 buff 已改为百分比口径:value 是百分比,需先换算为基础 hp 的绝对增量
return (value / 100) * hpEff * weights.W_HP;
case Attrs.critical:
return (value / 100) * (apEff / cd) * 0.5 * weights.W_DPS_PER_SEC * seconds;
case Attrs.critical_damage:
@@ -1154,7 +1156,7 @@ function calcSupportEffectValue(config: SkillConfig, apEff: number, cd: number,
/** 计算单条技能配置单次触发的效果价值 */
function calcSkillEffectValue(skill: SkillConfig, overrides: SkillOverrides | undefined, hero: heroInfo, targetLv: number, seconds: number): number {
const { ap: apEff } = getEffectiveStats(hero, targetLv);
const { hp: hpEff, ap: apEff } = getEffectiveStats(hero, targetLv);
const cd = getAttackCd(hero);
const config = overrides ? mergeSkillParams(skill, overrides) : skill;
const weights = HERO_POWER_WEIGHTS;
@@ -1168,7 +1170,7 @@ function calcSkillEffectValue(skill: SkillConfig, overrides: SkillOverrides | un
// 护盾为数值减免型:1 点护盾 ≈ 1 点预期伤害减免
return (config.ap || 0) * weights.W_shield;
case SkillKind.Support:
return calcSupportEffectValue(config, apEff, cd, seconds);
return calcSupportEffectValue(config, hpEff, apEff, cd, seconds);
default:
return 0;
}

View File

@@ -82,7 +82,7 @@ export class HeroAttrsComp extends ecs.Comp {
knockback_chance: number = 0; // 击退强化(原击退概率,现转换为击退距离增量)
knockback_distance: number = 0; // 击退距离强化
knockback_res: number = 0; // 击退抗性(削减击退距离增量)
crit_damage: number = 0; // 额外暴击伤害
critical_damage: number = 0; // 额外暴击伤害(与 Attrs.critical_damage 同名,供 add_special_attr 动态累加)
puncture_chance: number = 0; // 穿透概率
puncture_dmg_bonus: number = 0; // 穿透后伤害增量(百分点,乘算加成)
wfuny: number = 0; // 风怒
@@ -334,8 +334,8 @@ export class HeroAttrsComp extends ecs.Comp {
/** 英雄实时暴击伤害 = 基础额外暴伤 + 驻场暴伤。 */
public getRuntimeCritDamageBonus(): number {
if (this.fac !== FacSet.HERO) return this.crit_damage;
return this.crit_damage + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroCritDamage);
if (this.fac !== FacSet.HERO) return this.critical_damage;
return this.critical_damage + HeroAttrsComp.getFieldPercentValue(FieldSkillType.HeroCritDamage);
}
/** 攻速加成通过缩短普通攻击技能 CD 生效,正值越高,攻击越快。 */
@@ -606,7 +606,7 @@ export class HeroAttrsComp extends ecs.Comp {
this.knockback_chance = 0;
this.knockback_distance = 0;
this.knockback_res = 0;
this.crit_damage = 0;
this.critical_damage = 0;
this.revived_count = 0;
this.invincible_time = 0;
this.puncture_chance = 0;

View File

@@ -4,6 +4,7 @@ import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { DTType, RType, SkillConfig, SkillKind, SkillSet, TGroup, SkillOverrides, mergeSkillParams } from "../common/config/SkillSet";
import { Skill } from "../skill/Skill";
import { SkillView } from "../skill/SkillView";
import { smc } from "../common/SingletonModuleComp";
import { HeroDisVal, HeroInfo, HType } from "../common/config/heroSet";
import { Attrs } from "../common/config/HeroAttrs";
@@ -90,9 +91,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
targetEids = this.collectFriendlyTargetEids(FacSet.HERO, undefined, true); // 获取所有英雄阵营的目标
}
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2]
const cNum = Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
const castTimes = 1 + cNum;
// 召唤数量由技能 prefab 的 SkillView.summon_count 决定;召唤类技能(>1)默认不再参与风怒多发
const summonCount = this.resolveSummonCount(config);
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2];召唤类技能屏蔽风怒多发
const cNum = summonCount > 1 ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
const castTimes = Math.max(summonCount, 1 + cNum);
// 构造一个模拟的 HeroAttrsComp 用于数值计算,只包含基础卡牌伤害计算所需的属性
const mockAttrs = new HeroAttrsComp();
@@ -145,14 +148,23 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
mLogger.log(this.debugMode, "SCastSystem", "forceCastCardSkill: casting skill", s_uuid, "castTimes", castTimes, "targetPos", targetPos, "isFriendly", isFriendly, "buff_value", config.buff_value, "timed_buff_id", config.timed_buff_id);
for (let i = 0; i < castTimes; i++) {
if (isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(targetEids, FacSet.HERO);
mLogger.log(this.debugMode, "SCastSystem", "friendlyTargets count =", friendlyTargets.length);
if (friendlyTargets.length === 0) continue;
this.applyFriendlySkillEffects(s_uuid, cardLv, config, null as any, mockAttrs, friendlyTargets, spawnPos);
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*SUMMON_INTERVAL(卡牌无施法主体,用 setTimeout 并在回调内做效期校验)
const exec = () => {
if (!smc.mission.play || smc.mission.pause) return;
if (isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(targetEids, FacSet.HERO);
mLogger.log(this.debugMode, "SCastSystem", "friendlyTargets count =", friendlyTargets.length);
if (friendlyTargets.length === 0) return;
this.applyFriendlySkillEffects(s_uuid, cardLv, config, null as any, mockAttrs, friendlyTargets, spawnPos);
} else {
const enemyTargetPos = this.resolveRepeatCastTargetPos(targetPos, i);
this.createSkillEntityForCard(s_uuid, cardLv, mockAttrs, spawnPos, enemyTargetPos, i, overrides);
}
};
if (i === 0) {
exec();
} else {
const enemyTargetPos = this.resolveRepeatCastTargetPos(targetPos, i);
this.createSkillEntityForCard(s_uuid, cardLv, mockAttrs, spawnPos, enemyTargetPos, i, overrides);
setTimeout(exec, i * FightSet.SUMMON_INTERVAL * 1000);
}
}
}
@@ -184,6 +196,29 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
/** 查询缓存:避免每帧重复创建 matcher */
private heroMatcher: ecs.IMatcher | null = null;
/** 技能 prefab 召唤数量缓存:sp_name -> summon_count */
private summonCountCache: Map<string, number> = new Map();
/**
* 解析技能 prefab 上 SkillView.summon_count 配置的单次施法召唤数量。
* 仅使用已缓存的 prefab(oops.res.get 同步命中),未加载时回退 1(单体),不引入异步等待。
*/
private resolveSummonCount(config: SkillConfig): number {
const cached = this.summonCountCache.get(config.sp_name);
if (cached !== undefined) return cached;
const path = `game/skill/atk/${config.sp_name}`;
let count = 1;
const prefab = oops.res.get(path, Prefab) as Prefab | null;
if (prefab) {
const view = prefab.data?.getComponent(SkillView);
if (view) {
count = Math.max(1, Math.floor(view.summon_count));
}
this.summonCountCache.set(config.sp_name, count);
}
return count;
}
/** 获取英雄查询条件(包含属性与视图组件) */
private getHeroMatcher(): ecs.IMatcher {
if (!this.heroMatcher) {
@@ -273,9 +308,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2]
const cNum = Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
const castTimes = 1 + cNum;
// 召唤数量由技能 prefab 的 SkillView.summon_count 决定;召唤类技能(>1)默认不再参与风怒多发
const summonCount = this.resolveSummonCount(config);
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2];召唤类技能屏蔽风怒多发
const cNum = summonCount > 1 ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
const castTimes = Math.max(summonCount, 1 + cNum);
let val = ""
if (castTimes > 1) {
val = "*" + castTimes.toString
@@ -283,14 +320,19 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
heroView.skill_name(val, s_uuid, triggerType)
for (let i = 0; i < castTimes; i++) {
if (!heroView.node || !heroView.node.isValid) return;
if (isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(targetEids, heroAttrs.fac);
if (friendlyTargets.length === 0) continue;
this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null);
} else {
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*SUMMON_INTERVAL
heroView.scheduleOnce(() => {
if (!smc.mission.play || smc.mission.pause) return;
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
if (isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(targetEids, heroAttrs.fac);
if (friendlyTargets.length === 0) return;
this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null);
return;
}
const enemyTargetPos = this.resolveRepeatCastTargetPos(targetPos, i);
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides);
}
}, i * FightSet.SUMMON_INTERVAL);
}
}
@@ -343,12 +385,16 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
const skillLv = castPlan.skillLv;
const overrides = castPlan.overrides;
let config = SkillSet[s_uuid];
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2]
const cNum = Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
if (!config) return;
config = mergeSkillParams(config, overrides);
// 召唤数量由技能 prefab 的 SkillView.summon_count 决定;召唤类技能(>1)默认不再触发风怒
const summonCount = this.resolveSummonCount(config);
const isSummonSkill = summonCount > 1;
// 多发次数由 overrides.num 提供(技能卡差异化多发),限制在 [0, 2];召唤类技能屏蔽风怒多发
const cNum = isSummonSkill ? 0 : Math.min(2, Math.max(0, Math.floor(overrides?.num ?? 0)));
//播放前摇技能动画
heroView.playReady(config.readyAnm);
//播放角色攻击动画
@@ -356,8 +402,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
// 因为 castSkill 只由 update 循环调用,处理的必然是 heroAttrs.skills 中的普通技能
// 特殊触发技能(call/dead/atking等)走的是 forceCastTriggerSkill,不会调用 castSkill
// 召唤类技能(summon_count>1)不再计入普攻链,避免触发 atking(风怒类)附加技能
const skillIds = heroAttrs.getSkillIds();
if (skillIds.includes(s_uuid)) {
if (!isSummonSkill && skillIds.includes(s_uuid)) {
heroAttrs.atk_count++;
this.checkAndTriggerAtkingSkills(heroAttrs, heroView);
}
@@ -370,18 +417,21 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
heroView.scheduleOnce(() => {
if (!smc.mission.play || smc.mission.pause || !smc.mission.in_fight) return;
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
const castTimes = 1 + cNum;
const castTimes = Math.max(summonCount, 1 + cNum);
for (let i = 0; i < castTimes; i++) {
if (!smc.mission.play || smc.mission.pause || !smc.mission.in_fight) return;
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
if (castPlan.isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(castPlan.targetEids, heroAttrs.fac);
if (friendlyTargets.length === 0) return;
this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null);
continue;
}
const enemyTargetPos = this.resolveRepeatCastTargetPos(castPlan.targetPos, i);
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides);
// 多发/召唤按间隔错峰执行:i=0 立即,其余延迟 i*SUMMON_INTERVAL
heroView.scheduleOnce(() => {
if (!smc.mission.play || smc.mission.pause || !smc.mission.in_fight) return;
if (!heroView.node || !heroView.node.isValid || heroAttrs.is_dead) return;
if (castPlan.isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(castPlan.targetEids, heroAttrs.fac);
if (friendlyTargets.length === 0) return;
this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null);
return;
}
const enemyTargetPos = this.resolveRepeatCastTargetPos(castPlan.targetPos, i);
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides);
}, i * FightSet.SUMMON_INTERVAL);
}
}, delay);
heroAttrs.triggerSkillCD(s_uuid);
@@ -392,11 +442,14 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
SkillTriggerHelper.trigger(SkillTriggerType.Atking, heroAttrs, heroView);
}
/** 多发/召唤的纵向扇形偏移档位(像素),索引按 中/上/下/上上/下下... 循环 */
private static readonly REPEAT_Y_OFFSETS: number[] = [0, 15, -15, 30, -30, 45, -45, 60, -60];
private resolveRepeatCastTargetPos(targetPos: Vec3 | null, castIndex: number): Vec3 | null {
if (!targetPos) return null;
if (castIndex === 1) return new Vec3(targetPos.x, targetPos.y + 15, targetPos.z);
if (castIndex === 2) return new Vec3(targetPos.x, targetPos.y - 15, targetPos.z);
return targetPos;
const offset = SCastSystem.REPEAT_Y_OFFSETS[castIndex % SCastSystem.REPEAT_Y_OFFSETS.length];
if (offset === 0) return targetPos;
return new Vec3(targetPos.x, targetPos.y + offset, targetPos.z);
}
/** 构建技能尝试顺序:优先主动技能,其次普攻 */
@@ -431,9 +484,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
private resolveRepeatCastStartPos(startPos: Readonly<Vec3>, castIndex: number): Vec3 {
if (castIndex === 1) return new Vec3(startPos.x, startPos.y + 15, startPos.z);
if (castIndex === 2) return new Vec3(startPos.x, startPos.y - 15, startPos.z);
return startPos.clone();
const offset = SCastSystem.REPEAT_Y_OFFSETS[castIndex % SCastSystem.REPEAT_Y_OFFSETS.length];
if (offset === 0) return startPos.clone();
return new Vec3(startPos.x, startPos.y + offset, startPos.z);
}
/**
@@ -478,11 +531,6 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
} else if (kind === SkillKind.Shield && sAp !== 0) {
const addShield = Math.max(0, Math.floor(sAp));
model.add_shield(addShield);
} else if (kind === SkillKind.Gold) {
const addGold = config.gold ?? config.ap;
if (addGold > 0) {
MissionEconomy.addCoin(addGold);
}
}
if (config.buff_type !== undefined) {
@@ -503,18 +551,24 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
return;
}
// 永久 buff 的数值直接取 config.ap(已由 SkillConfig/SkillOverrides 提供最终值)
// 永久 buff 统一按百分比结算:攻击/生命按基础值相对放大,其余按百分点累加
const totalBuffValue = config.ap;
switch (config.buff_type) {
case Attrs.ap:
model.add_ap(totalBuffValue);
case Attrs.ap: {
// 攻击力按基础 ap 的百分比提升
const addAp = Math.floor(model.ap * totalBuffValue / 100);
model.add_ap(addAp);
break;
case Attrs.hp_max:
model.add_hp_max(totalBuffValue);
}
case Attrs.hp_max: {
// 最大生命按基础 hp_max 的百分比提升
const addHp = Math.floor(model.hp_max * totalBuffValue / 100);
model.add_hp_max(addHp);
break;
}
default:
// 除了 hp_max 和 ap,其他固定属性走统一的 add_special_attr 方法
// 暴击/击晕/穿透/攻速等概率类属性按百分点直接累加
model.add_special_attr(config.buff_type, totalBuffValue);
break;
}

View File

@@ -159,8 +159,17 @@ export class Skill extends ecs.Entity {
SView.group = caster.box_group
this.add(SView);
startPos.x = startPos.x + SView.atk_x * face
startPos.y = startPos.y + SView.atk_y
// 强制出生点:-1=不强制使用 atk_x/atk_y 偏移,>=0=强制世界坐标
if (SView.forceStartX >= 0) {
startPos.x = SView.forceStartX;
} else {
startPos.x = startPos.x + SView.atk_x * face;
}
if (SView.forceStartY >= 0) {
startPos.y = SView.forceStartY;
} else {
startPos.y = startPos.y + SView.atk_y;
}
node.setPosition(startPos);
@@ -179,7 +188,8 @@ export class Skill extends ecs.Entity {
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;
// 抛射物高度增量:叠加视图层 height_offset,抬高贝塞尔弹道顶点
sMoveCom.bezierMidHeight = (config.bezier_mid_y ?? sMoveCom.bezierMidHeight) + Math.max(0, SView.height_offset);
sMoveCom.bezierArc = config.bezier_arc ?? sMoveCom.bezierArc;
// 从SkillView获取移动参数,位置初始化由SMoveSystem统一处理
sMoveCom.atk_x = SView.atk_x;

View File

@@ -42,6 +42,22 @@ export class SkillView extends CCComp {
@property({ type: CCFloat, slide: true, range: [0, 2000, 10], tooltip: "直线飞行距离(仅 linear 生效)" })
distance: number = 0;
/** 强制出生点世界坐标X(-1=不强制,使用 atk_x 偏移;>=0 时强制使用该值,如 0=屏幕中心) */
@property({ type: CCFloat, tooltip: "强制出生点X:-1=不强制,>=0=强制世界坐标X" })
forceStartX: number = -1;
/** 强制出生点世界坐标Y(-1=不强制,使用 atk_y 偏移;>=0 时强制使用该值) */
@property({ type: CCFloat, tooltip: "强制出生点Y:-1=不强制,>=0=强制世界坐标Y" })
forceStartY: number = -1;
/** 召唤数量:SCastSystem 单次施法实例化的技能实体个数(<=1 为单体;>1 时按召唤物语义,不再参与风怒等额外触发) */
@property({ type: CCInteger, slide: true, range: [1, 8, 1], tooltip: "单次施法召唤的技能实体个数" })
summon_count: number = 1;
/** 抛射物高度增量(像素),贝塞尔弹道时叠加到 bezierMidHeight,控制抛物线顶点抬高 */
@property({ type: CCFloat, slide: true, range: [0, 500, 5], tooltip: "抛射物高度增量(叠加到贝塞尔弹道中点高度)" })
height_offset: number = 0;
private debugMode: boolean = false;
anim: Animation = null;