Merge origin/card0614 into card0614

合并远程药水限时强化系统与本地驻场光环配置,保留双方
BuffSet 新增条目(7111-7113 战前强攻 + 7010-7019
玩家计时性强化)。
This commit is contained in:
pan
2026-07-24 10:26:04 +08:00
18 changed files with 9520 additions and 5356 deletions

View File

@@ -31,29 +31,29 @@
"__id__": 64
},
{
"__id__": 90
"__id__": 96
},
{
"__id__": 98
"__id__": 104
},
{
"__id__": 116
"__id__": 122
}
],
"_active": true,
"_components": [
{
"__id__": 132
"__id__": 138
},
{
"__id__": 134
"__id__": 140
},
{
"__id__": 136
"__id__": 142
}
],
"_prefab": {
"__id__": 144
"__id__": 150
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -215,7 +215,7 @@
"_contentSize": {
"__type__": "cc.Size",
"width": 714,
"height": 94
"height": 114
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -565,7 +565,7 @@
"_contentSize": {
"__type__": "cc.Size",
"width": 712,
"height": 91
"height": 111
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -740,7 +740,7 @@
"_contentSize": {
"__type__": "cc.Size",
"width": 720,
"height": 100
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -862,7 +862,7 @@
"_contentSize": {
"__type__": "cc.Size",
"width": 720,
"height": 100
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -1023,7 +1023,7 @@
},
"_alignFlags": 8,
"_target": null,
"_left": 10.696000000000026,
"_left": 13.908000000000015,
"_right": 0,
"_top": 0,
"_bottom": 0,
@@ -1071,8 +1071,8 @@
],
"value": {
"__type__": "cc.Vec3",
"x": -309.304,
"y": 0,
"x": -306.092,
"y": 2.693,
"z": 0
}
},
@@ -1171,8 +1171,8 @@
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -271.748,
"y": 16.571,
"x": -265.787,
"y": 38.564,
"z": 0
},
"_lrot": {
@@ -1212,7 +1212,7 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 104,
"width": 84,
"height": 54.4
},
"_anchorPoint": {
@@ -1251,8 +1251,8 @@
"_string": "武器名称",
"_horizontalAlign": 0,
"_verticalAlign": 1,
"_actualFontSize": 25,
"_fontSize": 25,
"_actualFontSize": 20,
"_fontSize": 20,
"_fontFamily": "Arial",
"_lineHeight": 40,
"_overflow": 0,
@@ -1308,7 +1308,7 @@
},
"_alignFlags": 8,
"_target": null,
"_left": 88.25200000000001,
"_left": 94.21300000000002,
"_right": 0,
"_top": 0,
"_bottom": 0,
@@ -1356,31 +1356,31 @@
"__id__": 65
},
{
"__id__": 71
"__id__": 73
},
{
"__id__": 77
"__id__": 81
}
],
"_active": true,
"_components": [
{
"__id__": 83
"__id__": 89
},
{
"__id__": 85
"__id__": 91
},
{
"__id__": 87
"__id__": 93
}
],
"_prefab": {
"__id__": 89
"__id__": 95
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -160,
"y": 0,
"x": -265,
"y": -11.445000000000007,
"z": 0
},
"_lrot": {
@@ -1422,10 +1422,13 @@
},
{
"__id__": 68
},
{
"__id__": 70
}
],
"_prefab": {
"__id__": 70
"__id__": 72
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -1470,7 +1473,7 @@
},
"_contentSize": {
"__type__": "cc.Size",
"width": 300,
"width": 500,
"height": 23.333333333333332
},
"_anchorPoint": {
@@ -1552,6 +1555,42 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "b5sZHO3gNIW76AXUeqZYBn"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 65
},
"_enabled": true,
"__prefab": {
"__id__": 71
},
"_alignFlags": 40,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 300,
"_originalHeight": 0,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "90OrNvrelLZbOIOrrDJnEv"
},
{
"__type__": "cc.PrefabInfo",
"root": {
@@ -1577,14 +1616,17 @@
"_active": true,
"_components": [
{
"__id__": 72
"__id__": 74
},
{
"__id__": 74
"__id__": 76
},
{
"__id__": 78
}
],
"_prefab": {
"__id__": 76
"__id__": 80
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -1621,15 +1663,15 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 71
"__id__": 73
},
"_enabled": true,
"__prefab": {
"__id__": 73
"__id__": 75
},
"_contentSize": {
"__type__": "cc.Size",
"width": 300,
"width": 500,
"height": 23.333333333333332
},
"_anchorPoint": {
@@ -1649,11 +1691,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 71
"__id__": 73
},
"_enabled": true,
"__prefab": {
"__id__": 75
"__id__": 77
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -1711,6 +1753,42 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "dc6KYeWoJBr5NspO5kGx92"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 73
},
"_enabled": true,
"__prefab": {
"__id__": 79
},
"_alignFlags": 40,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 300,
"_originalHeight": 0,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ecZYYEuDRAh5fCYP3KMUKj"
},
{
"__type__": "cc.PrefabInfo",
"root": {
@@ -1736,14 +1814,17 @@
"_active": true,
"_components": [
{
"__id__": 78
"__id__": 82
},
{
"__id__": 80
"__id__": 84
},
{
"__id__": 86
}
],
"_prefab": {
"__id__": 82
"__id__": 88
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -1780,15 +1861,15 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 77
"__id__": 81
},
"_enabled": true,
"__prefab": {
"__id__": 79
"__id__": 83
},
"_contentSize": {
"__type__": "cc.Size",
"width": 300,
"width": 500,
"height": 23.333333333333332
},
"_anchorPoint": {
@@ -1808,11 +1889,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 77
"__id__": 81
},
"_enabled": true,
"__prefab": {
"__id__": 81
"__id__": 85
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -1870,6 +1951,42 @@
"__type__": "cc.CompPrefabInfo",
"fileId": "f0oseF23FJJY7bo5zkXU/L"
},
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 81
},
"_enabled": true,
"__prefab": {
"__id__": 87
},
"_alignFlags": 40,
"_target": null,
"_left": 0,
"_right": 0,
"_top": 0,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 300,
"_originalHeight": 0,
"_alignMode": 2,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "7dc/Tc5s5N+42zkFkChZxc"
},
{
"__type__": "cc.PrefabInfo",
"root": {
@@ -1893,11 +2010,11 @@
},
"_enabled": true,
"__prefab": {
"__id__": 84
"__id__": 90
},
"_contentSize": {
"__type__": "cc.Size",
"width": 420,
"width": 500,
"height": 70
},
"_anchorPoint": {
@@ -1921,14 +2038,14 @@
},
"_enabled": true,
"__prefab": {
"__id__": 86
"__id__": 92
},
"_alignFlags": 45,
"_target": null,
"_left": 200,
"_right": 100,
"_top": 15,
"_bottom": 15,
"_left": 95,
"_right": 125,
"_top": 36.445,
"_bottom": 13.554999999999996,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
@@ -1957,7 +2074,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 88
"__id__": 94
},
"_resizeMode": 1,
"_layoutType": 2,
@@ -2010,22 +2127,22 @@
"_active": true,
"_components": [
{
"__id__": 91
"__id__": 97
},
{
"__id__": 93
"__id__": 99
},
{
"__id__": 95
"__id__": 101
}
],
"_prefab": {
"__id__": 97
"__id__": 103
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -246.42299999999997,
"y": -14.317,
"x": -150.389,
"y": 39.317,
"z": 0
},
"_lrot": {
@@ -2057,11 +2174,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 90
"__id__": 96
},
"_enabled": true,
"__prefab": {
"__id__": 92
"__id__": 98
},
"_contentSize": {
"__type__": "cc.Size",
@@ -2085,11 +2202,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 90
"__id__": 96
},
"_enabled": true,
"__prefab": {
"__id__": 94
"__id__": 100
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -2153,15 +2270,15 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 90
"__id__": 96
},
"_enabled": true,
"__prefab": {
"__id__": 96
"__id__": 102
},
"_alignFlags": 8,
"_target": null,
"_left": 91.2922546983011,
"_left": 187.32625469830108,
"_right": 0,
"_top": 0,
"_bottom": 0,
@@ -2206,20 +2323,20 @@
},
"_children": [
{
"__id__": 99
"__id__": 105
},
{
"__id__": 107
"__id__": 113
}
],
"_active": true,
"_components": [
{
"__id__": 113
"__id__": 119
}
],
"_prefab": {
"__id__": 115
"__id__": 121
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -2256,23 +2373,23 @@
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 98
"__id__": 104
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 100
"__id__": 106
},
{
"__id__": 102
"__id__": 108
},
{
"__id__": 104
"__id__": 110
}
],
"_prefab": {
"__id__": 106
"__id__": 112
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -2309,11 +2426,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 99
"__id__": 105
},
"_enabled": true,
"__prefab": {
"__id__": 101
"__id__": 107
},
"_contentSize": {
"__type__": "cc.Size",
@@ -2337,11 +2454,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 99
"__id__": 105
},
"_enabled": true,
"__prefab": {
"__id__": 103
"__id__": 109
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -2382,11 +2499,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 99
"__id__": 105
},
"_enabled": true,
"__prefab": {
"__id__": 105
"__id__": 111
},
"_alignFlags": 45,
"_target": null,
@@ -2431,20 +2548,20 @@
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 98
"__id__": 104
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 108
"__id__": 114
},
{
"__id__": 110
"__id__": 116
}
],
"_prefab": {
"__id__": 112
"__id__": 118
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -2481,11 +2598,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 107
"__id__": 113
},
"_enabled": true,
"__prefab": {
"__id__": 109
"__id__": 115
},
"_contentSize": {
"__type__": "cc.Size",
@@ -2509,11 +2626,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 107
"__id__": 113
},
"_enabled": true,
"__prefab": {
"__id__": 111
"__id__": 117
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -2590,11 +2707,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 98
"__id__": 104
},
"_enabled": true,
"__prefab": {
"__id__": 114
"__id__": 120
},
"_contentSize": {
"__type__": "cc.Size",
@@ -2635,20 +2752,20 @@
},
"_children": [
{
"__id__": 117
"__id__": 123
},
{
"__id__": 123
"__id__": 129
}
],
"_active": true,
"_components": [
{
"__id__": 129
"__id__": 135
}
],
"_prefab": {
"__id__": 131
"__id__": 137
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -2685,20 +2802,20 @@
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 116
"__id__": 122
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 118
"__id__": 124
},
{
"__id__": 120
"__id__": 126
}
],
"_prefab": {
"__id__": 122
"__id__": 128
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -2735,11 +2852,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 117
"__id__": 123
},
"_enabled": true,
"__prefab": {
"__id__": 119
"__id__": 125
},
"_contentSize": {
"__type__": "cc.Size",
@@ -2763,11 +2880,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 117
"__id__": 123
},
"_enabled": true,
"__prefab": {
"__id__": 121
"__id__": 127
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -2821,20 +2938,20 @@
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 116
"__id__": 122
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 124
"__id__": 130
},
{
"__id__": 126
"__id__": 132
}
],
"_prefab": {
"__id__": 128
"__id__": 134
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -2871,11 +2988,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 123
"__id__": 129
},
"_enabled": true,
"__prefab": {
"__id__": 125
"__id__": 131
},
"_contentSize": {
"__type__": "cc.Size",
@@ -2899,11 +3016,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 123
"__id__": 129
},
"_enabled": true,
"__prefab": {
"__id__": 127
"__id__": 133
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -2980,11 +3097,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 116
"__id__": 122
},
"_enabled": true,
"__prefab": {
"__id__": 130
"__id__": 136
},
"_contentSize": {
"__type__": "cc.Size",
@@ -3025,12 +3142,12 @@
},
"_enabled": true,
"__prefab": {
"__id__": 133
"__id__": 139
},
"_contentSize": {
"__type__": "cc.Size",
"width": 720,
"height": 100
"height": 120
},
"_anchorPoint": {
"__type__": "cc.Vec2",
@@ -3053,7 +3170,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 135
"__id__": 141
},
"_alignFlags": 40,
"_target": null,
@@ -3089,10 +3206,10 @@
},
"_enabled": true,
"__prefab": {
"__id__": 137
"__id__": 143
},
"icon_node": {
"__id__": 138
"__id__": 144
},
"bg_node": null,
"name_label": {
@@ -3102,16 +3219,16 @@
"__id__": 68
},
"fied2": {
"__id__": 74
"__id__": 76
},
"fied3": {
"__id__": 80
"__id__": 84
},
"level_label": {
"__id__": 93
"__id__": 99
},
"buy_node": {
"__id__": 98
"__id__": 104
},
"_id": ""
},
@@ -3131,14 +3248,14 @@
"_active": true,
"_components": [
{
"__id__": 139
"__id__": 145
},
{
"__id__": 141
"__id__": 147
}
],
"_prefab": {
"__id__": 143
"__id__": 149
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -3175,11 +3292,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 138
"__id__": 144
},
"_enabled": true,
"__prefab": {
"__id__": 140
"__id__": 146
},
"_contentSize": {
"__type__": "cc.Size",
@@ -3203,11 +3320,11 @@
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 138
"__id__": 144
},
"_enabled": true,
"__prefab": {
"__id__": 142
"__id__": 148
},
"_customMaterial": null,
"_srcBlendFactor": 2,
@@ -3270,7 +3387,7 @@
"instance": null,
"targetOverrides": [
{
"__id__": 145
"__id__": 151
}
],
"nestedPrefabInstanceRoots": [
@@ -3282,7 +3399,7 @@
{
"__type__": "cc.TargetOverrideInfo",
"source": {
"__id__": 136
"__id__": 142
},
"sourceInfo": null,
"propertyPath": [
@@ -3292,7 +3409,7 @@
"__id__": 40
},
"targetInfo": {
"__id__": 146
"__id__": 152
}
},
{

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": "f820a186-b9f6-44df-a534-bdcbaa6a639a",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "hero"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -93,4 +93,76 @@ export const BuffList: Record<number, BuffConfig> = {
modifiers: [{ attr: Attrs.ap, op: ModOp.Flat, value: 90 }],
info: "攻击力 +90持续 10 秒",
},
// ==================== 玩家计时性强化(药水/技能用,默认 5s ====================
// 计时性回血HoT
7010: {
id: 7010, name: "再生", icon: "Stat_PotionBoost",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
tick: { interval: 0.5, damage_or_heal: 10, scale_by_ap_pct: 0 },
info: "每秒回复生命,持续 5 秒",
},
// 攻击强化(限时)
7011: {
id: 7011, name: "攻击爆发", icon: "Stat_Attack_03",
category: BuffCategory.Buff, duration: 5, max_stack: 1,
modifiers: [{ attr: Attrs.ap, op: ModOp.PercentAdd, value: 50 }],
info: "攻击力提升 50%,持续 5 秒",
},
// 暴击强化(限时)
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 }],
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 秒",
},
// 穿透强化(限时)
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 }],
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 }],
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 }],
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 }],
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 }],
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 }],
info: "冰冻抗性提升 50%,持续 5 秒",
},
};

View File

@@ -31,30 +31,25 @@ interface ItemCardRaw {
}
const ItemCardData: ItemCardRaw[] = [
// ==================== 基础恢复类 ====================
{ uuid: 9101, skill: 6302, name: "治疗药水", info: "恢复全体友方 300 点生命", cost: 3, weight: 20 },
{ uuid: 9102, skill: 6301, name: "护盾药水", info: "为全体友方添加护盾,可抵挡 3 次伤害", cost: 4, weight: 15 },
{ uuid: 9103, skill: 6303, name: "金币袋", info: "立即获得 5 金币", cost: 2, weight: 15, overrides: { gold: 5 } },
// ==================== 一次性回血(计时性 HoT5s ====================
{ uuid: 9101, skill: 6502, name: "再生药水", info: "全体友方每秒回复生命,持续 5 秒", cost: 0, weight: 20 },
// ==================== 性强化类(单次触发 ====================
{ uuid: 9111, skill: 6401, name: "攻击药", info: "全体友方攻击力提升 5", cost: 3, weight: 12 },
{ uuid: 9112, skill: 6402, name: "生命药剂", info: "全体友方最大生命值提升 20 点", cost: 3, weight: 12 },
{ uuid: 9113, skill: 6403, name: "暴击药剂", info: "全体友方击率提升 10%", cost: 3, weight: 12 },
{ uuid: 9114, skill: 6404, name: "暴伤药剂", info: "全体友方暴击伤害提升 20%", cost: 3, weight: 12 },
{ uuid: 9115, skill: 6405, name: "击晕药剂", info: "全体友方击晕概率提升 10%", cost: 3, weight: 12 },
{ uuid: 9116, skill: 6408, name: "穿刺药剂", info: "全体友方穿透概率提升 20%", cost: 3, weight: 12 },
{ uuid: 9117, skill: 6409, name: "风怒药剂", info: "全体友方风怒次数提升 1 次", cost: 3, weight: 12 },
// ==================== 计时性强化(统一 5s ====================
{ uuid: 9102, skill: 6503, name: "攻击药", info: "全体友方攻击力提升 50%,持续 5 秒", cost: 0, weight: 15 },
{ uuid: 9103, skill: 6504, name: "暴击药水", info: "全体友方暴击率提升 30%,持续 5 秒", cost: 0, weight: 15 },
{ uuid: 9104, skill: 6505, name: "击退药水", info: "全体友方击退概率提升 30%,持续 5 秒", cost: 0, weight: 12 },
{ uuid: 9105, skill: 6506, name: "穿透药水", info: "全体友方穿透概率提升 30%,持续 5 秒", cost: 0, weight: 12 },
{ uuid: 9106, skill: 6507, name: "攻速药水", info: "全体友方攻击速度提升 40%,持续 5 秒", cost: 0, weight: 15 },
{ uuid: 9107, skill: 6508, name: "风怒药水", info: "全体友方风怒概率提升 20%,持续 5 秒", cost: 0, weight: 12 },
{ uuid: 9108, skill: 6509, name: "击晕药水", info: "全体友方击晕概率提升 30%,持续 5 秒", cost: 0, weight: 12 },
{ uuid: 9109, skill: 6510, name: "击晕抗性药水", info: "全体友方击晕抗性提升 50%,持续 5 秒", cost: 4, weight: 10 },
{ uuid: 9110, skill: 6511, name: "冰冻抗性药水", info: "全体友方冰冻抗性提升 50%,持续 5 秒", cost: 4, weight: 10 },
// ==================== 高级恢复类 ====================
{ uuid: 9201, skill: 6302, name: "高级治疗药水", info: "恢复全体友方 600 点生命", cost: 6, weight: 8, overrides: { ap: 600 } },
{ uuid: 9202, skill: 6301, name: "高级护盾药水", info: "全体友方添加护盾,可抵挡 6 次伤害", cost: 8, weight: 6, overrides: { ap: 6 } },
{ uuid: 9203, skill: 6303, name: "大金币袋", info: "立即获得 12 金币", cost: 5, weight: 6, overrides: { gold: 12 } },
// ==================== 高级属性强化类 ====================
{ uuid: 9211, skill: 6401, name: "高级攻击药剂", info: "全体友方攻击力提升 10 点", cost: 6, weight: 5, overrides: { ap: 10 } },
{ uuid: 9212, skill: 6402, name: "高级生命药剂", info: "全体友方最大生命值提升 40 点", cost: 6, weight: 5, overrides: { ap: 40 } },
{ uuid: 9213, skill: 6403, name: "高级暴击药剂", info: "全体友方暴击率提升 20%", cost: 6, weight: 5, overrides: { ap: 2 } },
{ uuid: 9214, skill: 6404, name: "高级暴伤药剂", info: "全体友方暴击伤害提升 40%", cost: 6, weight: 5, overrides: { ap: 2 } },
// ==================== 高级buff_value 覆写,复用同一技能底座) ====================
{ uuid: 9201, skill: 6502, name: "高级再生药水", info: "全体友方每秒回复 25 生命,持续 5 秒", cost: 0, weight: 8, overrides: { buff_value: 25 } },
{ uuid: 9202, skill: 6503, name: "高级攻击药水", info: "全体友方攻击力提升 80%,持续 5 秒", cost: 0, weight: 8, overrides: { buff_value: 80 } },
{ uuid: 9203, skill: 6504, name: "高级暴击药水", info: "全体友方暴击率提升 50%,持续 5 秒", cost: 0, weight: 8, overrides: { buff_value: 50 } },
{ uuid: 9206, skill: 6507, name: "高级攻速药水", info: "全体友方攻击速度提升 70%,持续 5 秒", cost: 0, weight: 8, overrides: { buff_value: 70 } },
];
/**

View File

@@ -155,6 +155,7 @@ export interface SkillConfig {
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由药水卡自定义档位
call_hero?: number, // 召唤技能召唤英雄id(可选)
is_accel?: boolean, // 是否逐渐加速飞行
info: string, // 技能描述
@@ -172,6 +173,7 @@ export interface SkillOverrides {
bck?: number;
buff_type?: Attrs;
timed_buff_id?: number;
buff_value?: number;
call_hero?: number;
is_accel?: boolean;
}
@@ -377,6 +379,59 @@ export const SkillSet: Record<number, SkillConfig> = {
uuid: 6501, name: "复活", sp_name: "buff_wind", icon: "Stat_HolyDamage", TGroup: TGroup.Self, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 50, 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, info: "ap 代表复活的生命值百分比",
},
// ==================== 计时性强化技能(药水底座,统一 5s走 timed_buff_id ====================
// 注ap 字段在此类技能中无意义,修饰数值由 BuffList 配置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: 99, 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, timed_buff_id: 7010, info: "全体友方每秒回复生命持续5秒",
},
6503: {
uuid: 6503, 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: 0, hit_count: 99, 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, timed_buff_id: 7011, info: "全体友方攻击力提升50%持续5秒",
},
6504: {
uuid: 6504, name: "暴击爆发", sp_name: "buff_wind", icon: "Stat_CriticalChance_02", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 99, 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.critical, timed_buff_id: 7012, info: "全体友方暴击率提升30%持续5秒",
},
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: 99, 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秒",
},
6506: {
uuid: 6506, name: "穿透爆发", sp_name: "buff_wind", icon: "Stat_Tripleshot", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 99, 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_chance, timed_buff_id: 7014, info: "全体友方穿透概率提升30%持续5秒",
},
6507: {
uuid: 6507, name: "攻速爆发", sp_name: "buff_wind", icon: "Stat_AttackSpeed_02", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 99, 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.speed, timed_buff_id: 7015, info: "全体友方攻击速度提升40%持续5秒",
},
6508: {
uuid: 6508, name: "风怒爆发", sp_name: "buff_wind", icon: "Stat_CriticalComboChance", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 99, 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.wfuny, timed_buff_id: 7016, info: "全体友方风怒概率提升20%持续5秒",
},
6509: {
uuid: 6509, 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: 99, 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.stun_chance, timed_buff_id: 7017, info: "全体友方击晕概率提升30%持续5秒",
},
6510: {
uuid: 6510, name: "击晕抗性", sp_name: "buff_wind", icon: "Stat_Armor_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 99, 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.stun_res, timed_buff_id: 7018, info: "全体友方击晕抗性提升50%持续5秒",
},
6511: {
uuid: 6511, name: "冰冻抗性", sp_name: "buff_wind", icon: "Stat_Freeze", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 99, 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.freeze_res, timed_buff_id: 7019, info: "全体友方冰冻抗性提升50%持续5秒",
}
};

View File

@@ -15,12 +15,15 @@ export class ActiveBuff {
source_ap_snapshot: number;
/** 周期效果累计时间(秒) */
tick_acc: number = 0;
/** 修饰数值覆写(来自药水卡 buff_value优先于 BuffList 配置值undefined 表示用配置值 */
value_override?: number;
constructor(cfg_id: number, dur: number, src_uuid: number, src_ap: number) {
constructor(cfg_id: number, dur: number, src_uuid: number, src_ap: number, value_override?: number) {
this.config_id = cfg_id;
this.remaining = dur;
this.source_uuid = src_uuid;
this.source_ap_snapshot = src_ap;
this.value_override = value_override;
}
}

View File

@@ -4,19 +4,23 @@
* 设计说明:
* - 非 ECS 组件,是普通 class 单例(参考 smc 的单例暴露模式)。
* - 对外提供 applyBuff / dispel / getStack / hasControl 四组核心能力。
* - 控制类 buff 采用"双写"策略:既写入 BuffComp新框架也同步调用
* HeroAttrsComp.toFrost/toStun旧框架保证过渡期表现一致
* - 控制类 buff(冰冻/眩晕)统一写入 BuffComp由 BuffSystem 计时到期;
* 旧字段 frost_end_time/stun_end_time 已随 HeroBuffSystem 一并下线
*/
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { BuffCategory, BuffConfig, BuffList } from "../common/config/BuffSet";
import { FightSet } from "../common/config/GameSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { BuffCategory, BuffList } from "../common/config/BuffSet";
import { BuffComp, ActiveBuff } from "./BuffComp";
import { mLogger } from "../common/Logger";
/** applyBuff 的覆盖参数(预留按需覆写持续时间) */
/** Buff 模块调试日志开关 */
const DEBUG = false;
/** applyBuff 的覆盖参数(预留按需覆写持续时间 / 修饰数值) */
export interface BuffApplyOverrides {
duration?: number;
/** 覆写计时 buff 的修饰数值modifiers.value / tick.damage_or_heal由药水卡 buff_value 自定义档位 */
value?: number;
}
class BuffManagerImpl {
@@ -26,9 +30,7 @@ class BuffManagerImpl {
* 叠层规则:
* - max_stack === 0无限叠加始终 push 新层
* - existing.length < max_stackpush 新层
* - 已满:替换最老层(数组末尾)
*
* 控制类is_control额外双写旧字段frost/stun
* - 已满:移除最老层(数组首部)后追加新层,保持 FIFO 刷新语义
*
* @param target 目标实体
* @param buffId BuffList 配置 id
@@ -38,7 +40,11 @@ class BuffManagerImpl {
*/
applyBuff(target: ecs.Entity, buffId: number, sourceUuid: number = 0, sourceAp: number = 0, overrides?: BuffApplyOverrides): void {
const cfg = BuffList[buffId];
if (!cfg) return;
if (!cfg) {
mLogger.warn(true, "BuffManager", `applyBuff: buffId=${buffId} 配置不存在,跳过`);
return;
}
mLogger.log(DEBUG, "BuffManager", `applyBuff: buff=${cfg.name}(${buffId}) target_eid=${target.eid} duration=${overrides?.duration ?? cfg.duration} value_override=${overrides?.value} source_ap=${sourceAp}`);
const buffComp = this.ensureBuffComp(target);
const duration = overrides?.duration ?? cfg.duration;
@@ -49,21 +55,15 @@ class BuffManagerImpl {
buffComp.buffs.set(buffId, layers);
}
const newLayer = new ActiveBuff(buffId, duration, sourceUuid, sourceAp);
const newLayer = new ActiveBuff(buffId, duration, sourceUuid, sourceAp, overrides?.value);
if (cfg.max_stack === 0 || layers.length < cfg.max_stack) {
layers.push(newLayer);
} else {
// 已满,移除最老层(数组首部)后追加新层,保持 FIFO 刷新语义
layers.shift();
layers.push(newLayer);
}
// 控制类双写旧字段
if (cfg.is_control) {
this.applyControlEffect(target, cfg, duration);
}
buffComp.dirty_buffs = true;
}
@@ -80,8 +80,6 @@ class BuffManagerImpl {
const buffComp = target.get(BuffComp);
let removedKinds = 0;
const controlKindsCleared: ('frost' | 'stun')[] = [];
buffComp.buffs.forEach((layers, buffId) => {
const cfg = BuffList[buffId];
if (!cfg) return;
@@ -90,18 +88,8 @@ class BuffManagerImpl {
if (layers.length > 0) removedKinds++;
buffComp.buffs.delete(buffId);
// 记录被驱散的控制类型,用于后续重算
if (cfg.is_control && cfg.control_kind && cfg.control_kind !== 'none') {
controlKindsCleared.push(cfg.control_kind);
}
});
// 控制类驱散后重算旧字段
if (controlKindsCleared.length > 0) {
this.recomputeControlState(target, controlKindsCleared);
}
buffComp.dirty_buffs = true;
return removedKinds;
}
@@ -118,23 +106,15 @@ class BuffManagerImpl {
/**
* 判断目标是否处于指定类型的控制状态下。
* 同时检查新框架 BuffComp 和旧字段,兼容过渡期
* 唯一判定来源:BuffComp 中是否存在对应 control_kind 的活跃控制 buff
*/
hasControl(target: ecs.Entity, kind: 'frost' | 'stun'): boolean {
// 新框架:检查 BuffComp 中是否有对应控制类 buff
if (target.has(BuffComp)) {
const buffComp = target.get(BuffComp);
for (const [buffId, layers] of buffComp.buffs) {
if (layers.length === 0) continue;
const cfg = BuffList[buffId];
if (cfg?.is_control && cfg.control_kind === kind) return true;
}
}
// 旧字段兜底
if (target.has(HeroAttrsComp)) {
const attrs = target.get(HeroAttrsComp);
if (kind === 'frost') return attrs.isFrost();
if (kind === 'stun') return attrs.isStun();
if (!target.has(BuffComp)) return false;
const buffComp = target.get(BuffComp);
for (const [buffId, layers] of buffComp.buffs) {
if (layers.length === 0) continue;
const cfg = BuffList[buffId];
if (cfg?.is_control && cfg.control_kind === kind) return true;
}
return false;
}
@@ -150,59 +130,6 @@ class BuffManagerImpl {
}
return target.get(BuffComp);
}
/**
* 控制效果双写:将控制类 buff 同步写入旧字段。
* duration 会被换算成 toFrost/toStun 所需的倍数参数。
*/
private applyControlEffect(target: ecs.Entity, cfg: BuffConfig, duration: number): void {
if (!target.has(HeroAttrsComp)) return;
const attrs = target.get(HeroAttrsComp);
if (cfg.control_kind === 'frost') {
// toFrost(time) 内部会乘以 FROST_TIME这里反除得到倍数
attrs.toFrost(duration / FightSet.FROST_TIME);
} else if (cfg.control_kind === 'stun') {
attrs.toStun(duration / FightSet.STUN_TIME);
}
}
/**
* 驱散控制 buff 后重算旧字段。
* 如果对应类型的控制 buff 已全部清除,则立即将旧字段归零。
*
* @param target 目标实体
* @param clearedKinds 本次被驱散的控制类型列表
*/
private recomputeControlState(target: ecs.Entity, clearedKinds: ('frost' | 'stun')[]): void {
if (!target.has(HeroAttrsComp)) return;
const attrs = target.get(HeroAttrsComp);
// 检查是否还有同类型控制 buff 残留
const hasFrostInBuff = this.hasControlInBuff(target, 'frost');
const hasStunInBuff = this.hasControlInBuff(target, 'stun');
if (clearedKinds.includes('frost') && !hasFrostInBuff) {
attrs.frost_end_time = 0;
}
if (clearedKinds.includes('stun') && !hasStunInBuff) {
attrs.stun_end_time = 0;
}
}
/**
* 仅检查 BuffComp 中是否存在指定控制类型(不查旧字段)。
*/
private hasControlInBuff(target: ecs.Entity, kind: 'frost' | 'stun'): boolean {
if (!target.has(BuffComp)) return false;
const buffComp = target.get(BuffComp);
for (const [buffId, layers] of buffComp.buffs) {
if (layers.length === 0) continue;
const cfg = BuffList[buffId];
if (cfg?.is_control && cfg.control_kind === kind) return true;
}
return false;
}
}
/** Buff 管理器单例(全局唯一实例) */

View File

@@ -5,10 +5,9 @@
* - 遍历 BuffComp.buffs每层 remaining -= 步长
* - 若配置了 tick 效果DoT/HoT累计 tick_acc 并按 interval 结算伤害/治疗
* - 到期的层从数组中移除;数组空则清理 key
* - 兼容期:同时递减旧字段 frost_end_time / stun_end_time
*
* 注意:当前 HeroBuffSystem 仍在运行相同的 frost/stun 递减逻辑,
* 待旧系统完全下线后可移除此处兼容代码
* 控制类 buff冰冻/眩晕)不在此处做特殊处理:层存活期间 hasControl 判定
* 即生效,到期移除后控制自然解除。旧 HeroBuffSystem 已下线
*/
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
@@ -16,6 +15,10 @@ import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { BuffList } from "../common/config/BuffSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { BuffComp, ActiveBuff } from "./BuffComp";
import { mLogger } from "../common/Logger";
/** Buff 模块调试日志开关 */
const DEBUG = false;
/** 固定步长(秒) */
const TICK_STEP = 0.1;
@@ -61,6 +64,7 @@ export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (cfg.duration > 0 && layer.remaining <= 0) {
layers.splice(i, 1);
anyChanged = true;
mLogger.log(DEBUG, "BuffSystem", `buff 到期移除: ${cfg.name}(${buffId}) eid=${e.eid} 剩余层数=${layers.length}`);
}
}
@@ -91,8 +95,10 @@ export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
while (layer.tick_acc >= tick.interval) {
layer.tick_acc -= tick.interval;
// 按 ap 缩放后取整floor(dmg * (1 + ap_snapshot * scale_pct / 10000))
// damage_or_heal 优先取药水卡覆写值
const baseAmount = layer.value_override !== undefined ? layer.value_override : tick.damage_or_heal;
const scale = 1 + layer.source_ap_snapshot * tick.scale_by_ap_pct / 10000;
const amount = Math.floor(tick.damage_or_heal * scale);
const amount = Math.floor(baseAmount * scale);
attrs.add_hp(amount);
}
}

View File

@@ -200,14 +200,14 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
targetView.playEnd(skillConf.endAnm);
if (isFrost) {
TAttrsComp.toFrost();
targetView.in_iced(TAttrsComp.frost_end_time);
targetView.in_iced(FightSet.FROST_TIME);
if (damageEvent.Attrs.fac === FacSet.HERO) {
smc.vmdata.scores.freeze_count++;
}
}
if (isStun) {
TAttrsComp.toStun();
targetView.in_stun(TAttrsComp.stun_end_time);
targetView.in_stun(FightSet.STUN_TIME);
if (damageEvent.Attrs.fac === FacSet.HERO) {
smc.vmdata.scores.stun_count++;
}

View File

@@ -1,7 +1,6 @@
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { HeroDisVal, HeroInfo, HSkillInfo, HType, SkillTriggerType } from "../common/config/heroSet";
import { mLogger } from "../common/Logger";
import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { FacSet, FightSet } from "../common/config/GameSet";
import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
import { smc } from "../common/SingletonModuleComp";
@@ -9,6 +8,11 @@ import { Attrs } from "../common/config/HeroAttrs";
import { FieldSkillHelper } from "./FieldSkillHelper";
import { BuffComp } from "./BuffComp";
import { BuffList, ModOp } from "../common/config/BuffSet";
import { buffManager } from "./BuffManager";
/** 控制类 buff 配置 id对应 BuffList */
const BUFF_ID_FROST = 7003;
const BUFF_ID_STUN = 7004;
@ecs.register('HeroAttrs')
export class HeroAttrsComp extends ecs.Comp {
public debugMode: boolean = false;
@@ -66,9 +70,6 @@ export class HeroAttrsComp extends ecs.Comp {
invincible_time: number = 0;// 无敌时间
frost_end_time: number = 0;
stun_end_time: number = 0;
boom: boolean = false; // 自爆怪
// ==================== 脏标签标记 ====================
@@ -108,8 +109,6 @@ export class HeroAttrsComp extends ecs.Comp {
* 从 HeroInfo 读取初始配置,建立属性系统
*/
initAttrs() {
this.frost_end_time = 0;
this.stun_end_time = 0;
}
/*******************基础属性管理********************/
@@ -169,14 +168,16 @@ export class HeroAttrsComp extends ecs.Comp {
}
/** 施加冰冻:委托 buffManager 写入 BuffCompduration = FROST_TIME * time */
toFrost(time: number = 1) {
const frostTime = FightSet.FROST_TIME * time;
this.frost_end_time = Math.max(this.frost_end_time, frostTime);
if (!this.ent) return;
buffManager.applyBuff(this.ent, BUFF_ID_FROST, 0, 0, { duration: FightSet.FROST_TIME * time });
}
/** 施加击晕:委托 buffManager 写入 BuffComp并清零技能 CD */
toStun(time: number = 1) {
const stunTime = FightSet.STUN_TIME * time;
this.stun_end_time = Math.max(this.stun_end_time, stunTime);
if (!this.ent) return;
buffManager.applyBuff(this.ent, BUFF_ID_STUN, 0, 0, { duration: FightSet.STUN_TIME * time });
// 击晕时 CD 清零
for (const key in this.skills) {
@@ -217,11 +218,13 @@ export class HeroAttrsComp extends ecs.Comp {
skill.ccd = Math.min(actualCd, skill.ccd + dt);
}
}
/** 是否处于冰冻状态(统一走 BuffComp 判定) */
isFrost(): boolean {
return this.frost_end_time > 0
return !!this.ent && buffManager.hasControl(this.ent, 'frost');
}
/** 是否处于击晕状态(统一走 BuffComp 判定) */
isStun(): boolean {
return this.stun_end_time > 0
return !!this.ent && buffManager.hasControl(this.ent, 'stun');
}
getSkillLevel(skillId: number): number {
if (!skillId) return 0;
@@ -350,14 +353,17 @@ export class HeroAttrsComp extends ecs.Comp {
if (!cfg || !cfg.modifiers) return;
// 每层独立贡献,模拟叠层放大效果
for (let i = 0; i < layers.length; i++) {
const layerVal = layers[i].value_override; // 药水卡覆写值优先
for (const mod of cfg.modifiers) {
if (mod.attr !== attr) continue;
const value = layerVal !== undefined ? layerVal : mod.value;
mLogger.log(this.debugMode, "HeroAttrs", `aggregateTimedMods 命中: ${this.hero_name} buff=${cfg.name} attr=${attr} op=${mod.op} value=${value} remaining=${layers[i].remaining.toFixed(1)}`);
if (mod.op === ModOp.Flat) {
result.flatSum += mod.value;
result.flatSum += value;
} else {
// PercentAdd 与 PercentMul 暂统一按加法百分比聚合;
// PercentMul 真正的独立乘区尚未启用TODO: 启用时需改造为多乘区连乘
result.pctSum += mod.value;
result.pctSum += value;
}
}
}
@@ -372,7 +378,11 @@ export class HeroAttrsComp extends ecs.Comp {
public getFinalAp(): number {
const runtimeAp = this.getRuntimeAp(this.ap);
const mods = this.aggregateTimedMods(Attrs.ap);
return (runtimeAp + mods.flatSum) * (1 + mods.pctSum / 100);
const final = (runtimeAp + mods.flatSum) * (1 + mods.pctSum / 100);
if (this.debugMode && (mods.flatSum !== 0 || mods.pctSum !== 0)) {
mLogger.log(this.debugMode, "HeroAttrs", `getFinalAp: ${this.hero_name} base=${this.ap} runtime=${runtimeAp.toFixed(1)} flat=${mods.flatSum} pct=${mods.pctSum} final=${final.toFixed(1)}`);
}
return final;
}
/**
@@ -471,7 +481,9 @@ export class HeroAttrsComp extends ecs.Comp {
const skill = this.skills[skillId];
if (!skill) return 0;
if (skill.cd <= 0) return 0;
const speedBonus = this.getRuntimeAttackSpeedBonus();
// 攻速 = 驻场加成 + 计时性 buff 修饰speed 属性的 flat/pct 均按攻速百分点处理)
const timedMods = this.aggregateTimedMods(Attrs.speed);
const speedBonus = this.getRuntimeAttackSpeedBonus() + timedMods.flatSum + timedMods.pctSum;
if (speedBonus <= 0) return skill.cd;
const speedRate = 1 + speedBonus / 100;
return Math.max(HeroAttrsComp.minAttackCd, skill.cd / speedRate);
@@ -549,9 +561,6 @@ export class HeroAttrsComp extends ecs.Comp {
this.wfuny = 0;
this.boom = false;
this.frost_end_time = 0;
this.stun_end_time = 0;
// 重置技能距离缓存
this.maxSkillDistance = 0;
this.minSkillDistance = 0;
@@ -580,32 +589,5 @@ export class HeroAttrsComp extends ecs.Comp {
}
}
@ecs.register('HeroBuffSystem')
export class HeroBuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
private timer = new Timer(0.1)
filter(): ecs.IMatcher {
return ecs.allOf(HeroAttrsComp);
}
update(e: ecs.Entity): void {
if (this.timer.update(this.dt)) {
const attrsComp = e.get(HeroAttrsComp);
if (attrsComp.frost_end_time > 0) {
attrsComp.frost_end_time -= 0.1;
if (attrsComp.frost_end_time <= 0) {
attrsComp.frost_end_time = 0;
}
}
if (attrsComp.stun_end_time > 0) {
attrsComp.stun_end_time -= 0.1;
if (attrsComp.stun_end_time <= 0) {
attrsComp.stun_end_time = 0;
}
}
}
void e;
}
}

View File

@@ -13,6 +13,7 @@ import { GameEvent } from "../common/config/GameEvent";
import { SkillTriggerType } from "../common/config/heroSet";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
import { MissionEconomy } from "../map/MissionEconomy";
import { mLogger } from "../common/Logger";
import { MissionHeroComp } from "../map/MissionHeroComp";
import { buffManager } from "./BuffManager";
@@ -142,10 +143,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
console.log("[SCastSystem] forceCastCardSkill: casting skill", s_uuid, "castTimes", castTimes, "targetPos", targetPos);
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);
} else {
@@ -455,12 +457,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
private applyActualFriendlyEffect(target: HeroViewComp, kind: SkillKind, sAp: number, _cAttrsComp: HeroAttrsComp, config: SkillConfig, sUp: any, _skillLv: number = 1) {
if (!target.ent) return;
const model = target.ent.get(HeroAttrsComp);
if (!model || model.is_dead) return;
mLogger.log(this.debugMode, "SCastSystem", `applyActualFriendlyEffect: skill=${config.uuid} kind=${kind} buff_type=${config.buff_type} timed_buff_id=${config.timed_buff_id} buff_value=${config.buff_value} target=${model.hero_name}`);
if (config.endAnm && config.endAnm !== "") {
target.playEnd(config.endAnm);
@@ -493,7 +494,8 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
target.ent,
config.timed_buff_id,
_cAttrsComp.hero_uuid,
_cAttrsComp.ap
_cAttrsComp.ap,
config.buff_value !== undefined ? { value: config.buff_value } : undefined
);
return;
}

View File

@@ -355,8 +355,9 @@ export class HInfoComp extends CCComp {
}
// ---- 数值标签 ----
// 攻击力显示最终值(含计时 buff 修饰),实时反映药水等限时强化
if (this.apLabel) {
const currentAp = Math.max(0, Math.floor(this.model.ap ?? 0));
const currentAp = Math.max(0, Math.floor(this.model.getFinalAp()));
const baseAp = Math.max(0, Math.floor(this.model.base_ap ?? 0));
this.apLabel.string = `${currentAp}`;
if (this.apPlusLabel) {
@@ -370,7 +371,7 @@ export class HInfoComp extends CCComp {
}
}
if (this.hpLabel) {
const currentHp = Math.max(0, Math.floor(this.model.hp_max ?? 0));
const currentHp = Math.max(0, Math.floor(this.model.getFinalHpMax()));
const baseHp = Math.max(0, Math.floor(this.model.base_hp ?? 0));
this.hpLabel.string = `${currentHp}`;
if (this.hpPlusLabel) {

View File

@@ -28,6 +28,9 @@ import { SBox } from "./SBox";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { BoxSet } from "../common/config/GameSet";
import { SkillOverrides, SkillSet } from "../common/config/SkillSet";
import { smc } from "../common/SingletonModuleComp";
import { buffManager } from "../hero/BuffManager";
const { ccclass, property } = _decorator;
/** 技能槽位数据结构 */
@@ -73,6 +76,7 @@ export class MissSkillsComp extends CCComp {
/** 注册事件监听 */
onLoad() {
oops.message.on(GameEvent.UseSkillCard, this.onUseSkillCard, this);
oops.message.on(GameEvent.UseItemCard, this.onUseItemCard, this);
oops.message.on(GameEvent.RemoveSkillBox, this.onRemoveSkillBox, this);
}
@@ -80,6 +84,7 @@ export class MissSkillsComp extends CCComp {
onDestroy() {
super.onDestroy();
oops.message.off(GameEvent.UseSkillCard, this.onUseSkillCard, this);
oops.message.off(GameEvent.UseItemCard, this.onUseItemCard, this);
oops.message.off(GameEvent.RemoveSkillBox, this.onRemoveSkillBox, this);
}
@@ -138,10 +143,48 @@ export class MissSkillsComp extends CCComp {
*/
private onUseSkillCard(event: string, args: any) {
const payload = args ?? event;
const uuid = Number(payload?.uuid ?? 0);
// 卡 uuid 仅用于查配置;真正施法用技能 idpayload.skill缺省时退回 uuid兼容技能卡直接用技能 id 的场景)
const skillUuid = Number(payload?.skill ?? payload?.uuid ?? 0);
const card_lv = Math.max(1, Math.floor(Number(payload?.card_lv ?? 1)));
if (!uuid) return;
this.addSkill(uuid, card_lv);
if (!skillUuid) return;
// 药水/技能卡的 overrides含 buff_value需透传到技能实体
this.addSkill(skillUuid, card_lv, payload?.overrides);
}
/**
* 处理药水卡使用事件:直接对全队存活英雄施加计时 buff不经过 SBox 施法流程。
*
* 设计:
* 药水是"即时全队强化",无需节点、动画、槽位;
* 从技能配置取 timed_buff_id遍历 heroGrid 对每个存活英雄 applyBuff
* buff_value 覆写值透传到 ActiveBuff.value_override。
*
* @param event 事件名
* @param args 卡牌数据(含 skill、overrides
*/
private onUseItemCard(event: string, args: any) {
const payload = args ?? event;
const skillId = Number(payload?.skill ?? 0);
const config = SkillSet[skillId];
if (!config) {
mLogger.warn(this.debugMode, "MissSkillsComp", `potion skill ${skillId} config not found`);
return;
}
const buffId = config.timed_buff_id;
if (buffId === undefined) {
mLogger.warn(this.debugMode, "MissSkillsComp", `potion skill ${skillId} has no timed_buff_id`);
return;
}
const buffValue = payload?.overrides?.buff_value;
let applied = 0;
for (const eid of smc.mission.heroGrid) {
if (eid <= 0) continue;
const entity = ecs.getEntityByEid(eid);
if (!entity) continue;
buffManager.applyBuff(entity, buffId, 0, 0, buffValue !== undefined ? { value: buffValue } : undefined);
applied++;
}
mLogger.log(this.debugMode, "MissSkillsComp", `potion applied: skill=${skillId} buff=${buffId} value=${buffValue} heroes=${applied}`);
}
start() {
@@ -157,7 +200,7 @@ export class MissSkillsComp extends CCComp {
* @param uuid 技能 UUID
* @param card_lv 技能卡等级
*/
addSkill(uuid: number, card_lv: number) {
addSkill(uuid: number, card_lv: number, overrides?: SkillOverrides) {
if (!this.skill_box) {
mLogger.error(this.debugMode, "MissSkillsComp", "skill_box prefab not set");
return;
@@ -174,7 +217,7 @@ export class MissSkillsComp extends CCComp {
// 使用 ECS 实体创建技能节点
let sbox = ecs.getEntity<SBox>(SBox);
let pos = new Vec3(this.slots[emptyIndex].x, this.slots[emptyIndex].y, 0);
let node = sbox.load(uuid, card_lv, pos, this.skill_box);
let node = sbox.load(uuid, card_lv, pos, this.skill_box, overrides);
this.slots[emptyIndex].used = true;
this.slots[emptyIndex].node = node;

View File

@@ -2,6 +2,7 @@ import { instantiate, Prefab, Vec3, Node } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { smc } from "../common/SingletonModuleComp";
import { SkillBoxComp } from "./SkillBoxComp";
import { SkillOverrides } from "../common/config/SkillSet";
/** SBox 实体:负责技能卡效果节点创建、初始化与销毁流程 */
@ecs.register(`SBox`)
@@ -29,10 +30,10 @@ export class SBox extends ecs.Entity {
* 1) 创建节点并挂到 SKILL 层
* 2) 初始化表现与属性数据
*/
load(uuid: number, card_lv: number, pos: Vec3, prefab: Prefab): Node {
load(uuid: number, card_lv: number, pos: Vec3, prefab: Prefab, overrides?: SkillOverrides): Node {
let node = instantiate(prefab);
let scene = smc.map.MapView.scene;
// 统一挂到实体显示层 SKILL 节点下
let parent = scene.entityLayer!.node!.getChildByName("SKILL");
if (parent) {
@@ -43,9 +44,9 @@ export class SBox extends ecs.Entity {
// 获取并注册组件
let sboxComp = node.getComponent(SkillBoxComp) || node.addComponent(SkillBoxComp);
this.add(sboxComp);
// 初始化业务逻辑
sboxComp.init(uuid, card_lv);
// 初始化业务逻辑(透传药水卡 overrides含 buff_value
sboxComp.init(uuid, card_lv, overrides);
return node;
}

View File

@@ -166,10 +166,10 @@ export class SkillBoxComp extends CCComp {
* @param uuid 卡牌 UUID
* @param card_lv 技能卡等级
*/
init(uuid: number, card_lv: number) {
init(uuid: number, card_lv: number, overrides?: SkillOverrides) {
this.card_lv = card_lv;
// 查询触发配置
// 查询触发配置(药水卡不在 SkillCardList会走 else 分支)
const config = SkillCardList.find(c => c.uuid === uuid);
if (config) {
this.s_uuid = config.skill ?? uuid; // 获取实际的技能 UUID
@@ -177,14 +177,18 @@ export class SkillBoxComp extends CCComp {
this.trigger_times = config.t_times ?? 1;
this.trigger_interval = config.t_inv ?? 0;
this.keep_waves = config.keep_waves ?? 0;
this.overrides = config.overrides;
this.overrides = overrides ?? config.overrides; // 外部透传(药水卡)优先
this.field = config.field || [];
this.card_icon = config.icon; // 保存卡牌自定义图标(优先级最高)
// 读取触发类型与上限(兜底默认值,避免 undefined
this.trigger_type = config.trigger_type ?? CardTriggerType.Instant;
this.trigger_limit = config.trigger_limit ?? Infinity;
} else {
// 药水卡等uuid 即技能 id触发类型默认 Instant即时生效一次
this.s_uuid = uuid;
this.overrides = overrides;
this.trigger_type = CardTriggerType.Instant;
this.trigger_times = 1;
}
this.current_trigger_times = 0;