Compare commits
27 Commits
a5dc1ef540
...
new2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1c37eb510 | ||
|
|
2d11246838 | ||
|
|
d759689269 | ||
|
|
e78f4365e7 | ||
|
|
34a62f0a87 | ||
|
|
1988ae5ef1 | ||
|
|
df0dedba75 | ||
|
|
4fee02d82e | ||
|
|
15d14f7d59 | ||
|
|
9579c4f75e | ||
|
|
d54e6ce1d6 | ||
|
|
d06c99e2f1 | ||
|
|
76082d8e1f | ||
|
|
695a9e2b66 | ||
|
|
c6b5e1431f | ||
|
|
7fa18329d6 | ||
|
|
a2c397120c | ||
|
|
cb2a72da52 | ||
|
|
9da3e2d9fd | ||
|
|
eb4b370533 | ||
|
|
a3375ec3b6 | ||
|
|
011e7b3dde | ||
|
|
038abdc5d2 | ||
|
|
c27c81cfa6 | ||
|
|
a905db8e48 | ||
|
|
3060ee7df7 | ||
|
|
ab6a3b7824 |
@@ -38,10 +38,13 @@
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 110
|
||||
},
|
||||
{
|
||||
"__id__": 112
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 112
|
||||
"__id__": 114
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
@@ -95,7 +98,7 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 9.114,
|
||||
"x": 0,
|
||||
"y": 10,
|
||||
"z": 0
|
||||
},
|
||||
@@ -217,7 +220,7 @@
|
||||
"__id__": 17
|
||||
}
|
||||
],
|
||||
"_active": false,
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 25
|
||||
@@ -235,7 +238,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": -9.738,
|
||||
"y": -7.84,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -287,7 +290,7 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -35,
|
||||
"x": -20,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
@@ -328,8 +331,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 70,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 5
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -506,8 +509,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 240,
|
||||
"height": 40
|
||||
"width": 140,
|
||||
"height": 23.333333333333336
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -631,8 +634,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 70,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 5
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -859,7 +862,7 @@
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 40,
|
||||
"height": 11
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1037,7 +1040,7 @@
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 140,
|
||||
"height": 43.333333333333336
|
||||
"height": 36.66666666666667
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1162,7 +1165,7 @@
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 40,
|
||||
"height": 11
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1347,7 +1350,7 @@
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -25,
|
||||
"x": -20,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
@@ -1388,8 +1391,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 50,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1691,8 +1694,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 50,
|
||||
"height": 10
|
||||
"width": 40,
|
||||
"height": 9
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -1813,7 +1816,7 @@
|
||||
"__id__": 99
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_active": false,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 105
|
||||
@@ -2520,8 +2523,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 70,
|
||||
"height": 20
|
||||
"width": 40,
|
||||
"height": 11
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -2534,6 +2537,33 @@
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "cdvV5hAkdEaY6AT6urHKuf"
|
||||
},
|
||||
{
|
||||
"__type__": "c4181/FEWhKbY0D0PLdCyKi",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 113
|
||||
},
|
||||
"hpProgressBar": {
|
||||
"__id__": 53
|
||||
},
|
||||
"lvLabel": null,
|
||||
"shieldProgressBar": {
|
||||
"__id__": 77
|
||||
},
|
||||
"hpBarSprite": {
|
||||
"__id__": 36
|
||||
},
|
||||
"_id": "a77QVRkP5NX4b+LyqmW1KJ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "3eCKBFOJhLHKqcnak/mi3d"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
|
||||
@@ -1398,7 +1398,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21.504,
|
||||
"y": -31.947,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -1875,7 +1875,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 21.504,
|
||||
"y": -50.813,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -2352,7 +2352,7 @@
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"y": -150,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
@@ -2618,6 +2618,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "6bE0ieRMBGyYHdxvuJ03FA",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -3432,6 +3434,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "256TxMVzhHQopA08O7v8nq",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -3471,6 +3475,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "5dWwAe3IFHRJuoSicXBEs8",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -3710,6 +3716,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "b9zLdCeJlE5pPiK++dLgBX",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -4388,6 +4396,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "9d2p/canpFGof2O/w8JyTj",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -4427,6 +4437,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "6bkL20JyRDOI6ycVYKl/ie",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -4495,6 +4507,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "14GnCNU65BbJPIzLWE/Oiz",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -7363,6 +7377,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c4IqMKgXNExa5w4EAciRxE",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -8202,7 +8218,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "LINE2",
|
||||
"_name": "BOSS",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8273,7 +8289,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ea767WaZRHo4s0bZQfN8zP"
|
||||
"fileId": "3fVB8r2UFAB5QujD6LOw6s"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8283,14 +8299,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "60UdPtJn9ALLHCveLwT1bT",
|
||||
"fileId": "f4iFaXCXNKorfGJFH32POa",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "HERO",
|
||||
"_name": "MON",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8361,7 +8377,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "dasmpuTSVM7qA3QW9/3XjQ"
|
||||
"fileId": "ea767WaZRHo4s0bZQfN8zP"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8371,14 +8387,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "eaiasKNEFO6qvodGhL4a0b",
|
||||
"fileId": "60UdPtJn9ALLHCveLwT1bT",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "LINE1",
|
||||
"_name": "HERO",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8449,7 +8465,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "b8C5CFg5lM4oUHMeSrc/sV"
|
||||
"fileId": "dasmpuTSVM7qA3QW9/3XjQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8459,14 +8475,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "4dcNJaDM1CLI9q3fIqHZ/U",
|
||||
"fileId": "eaiasKNEFO6qvodGhL4a0b",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "LINE3",
|
||||
"_name": "LINE1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8537,7 +8553,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "a5LMIIbHtOKK10K5do5dSQ"
|
||||
"fileId": "b8C5CFg5lM4oUHMeSrc/sV"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8547,14 +8563,14 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "a6CBSkrzZIurlX0D/HzXDp",
|
||||
"fileId": "4dcNJaDM1CLI9q3fIqHZ/U",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "BOOS",
|
||||
"_name": "LINE3",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
@@ -8625,7 +8641,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "3fVB8r2UFAB5QujD6LOw6s"
|
||||
"fileId": "a5LMIIbHtOKK10K5do5dSQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
@@ -8635,7 +8651,7 @@
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "f4iFaXCXNKorfGJFH32POa",
|
||||
"fileId": "a6CBSkrzZIurlX0D/HzXDp",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
@@ -8893,6 +8909,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "35iAcUUYpE8akTtTCX1ITo",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
@@ -8932,6 +8950,8 @@
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "68Q5vEQWBINrggngtTZ7wh",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,21 +7,255 @@
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 8,
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 2,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0,
|
||||
"_duration": 0.125,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [],
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [],
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 1
|
||||
"__id__": 12
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.VectorTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channels": [
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
{
|
||||
"__id__": 6
|
||||
},
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
}
|
||||
],
|
||||
"_nComponents": 3
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
"scale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.08333333333333333,
|
||||
0.125
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.2999999523162842,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.2999999523162842,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"tangentMode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.2999999523162842,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.08333333333333333,
|
||||
0.125
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.100000023841858,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.100000023841858,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"tangentMode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1.100000023841858,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.08333333333333333,
|
||||
0.125
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": {
|
||||
"tangentMode": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealKeyframeValue",
|
||||
"interpolationMode": 0,
|
||||
"tangentWeightMode": 0,
|
||||
"value": 1,
|
||||
"rightTangent": 0,
|
||||
"rightTangentWeight": 1,
|
||||
"leftTangent": 0,
|
||||
"leftTangentWeight": 1,
|
||||
"easingMethod": 0,
|
||||
"__editorExtras__": null
|
||||
}
|
||||
],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RealCurve",
|
||||
"_times": [],
|
||||
"_values": [],
|
||||
"preExtrapolation": 1,
|
||||
"postExtrapolation": 1
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
|
||||
116
assets/resources/game/skill/anm/atk/atk02.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk02.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk02.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk02.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk02"
|
||||
}
|
||||
}
|
||||
116
assets/resources/game/skill/anm/atk/atk03.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk03.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk03.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk03.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk03"
|
||||
}
|
||||
}
|
||||
116
assets/resources/game/skill/anm/atk/atk04.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk04.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk04.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk04.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk04"
|
||||
}
|
||||
}
|
||||
116
assets/resources/game/skill/anm/atk/atk05.anim
Normal file
116
assets/resources/game/skill/anm/atk/atk05.anim
Normal file
@@ -0,0 +1,116 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.AnimationClip",
|
||||
"_name": "atk01",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {
|
||||
"embeddedPlayerGroups": []
|
||||
},
|
||||
"_native": "",
|
||||
"sample": 24,
|
||||
"speed": 1,
|
||||
"wrapMode": 1,
|
||||
"enableTrsBlending": false,
|
||||
"_duration": 0.20833333333333334,
|
||||
"_hash": 500763545,
|
||||
"_tracks": [
|
||||
{
|
||||
"__id__": 1
|
||||
}
|
||||
],
|
||||
"_exoticAnimation": null,
|
||||
"_events": [
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "",
|
||||
"params": []
|
||||
},
|
||||
{
|
||||
"frame": 0.08333333333333333,
|
||||
"func": "atk",
|
||||
"params": []
|
||||
}
|
||||
],
|
||||
"_embeddedPlayers": [],
|
||||
"_additiveSettings": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_auxiliaryCurveEntries": []
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ObjectTrack",
|
||||
"_binding": {
|
||||
"__type__": "cc.animation.TrackBinding",
|
||||
"path": {
|
||||
"__id__": 2
|
||||
},
|
||||
"proxy": null
|
||||
},
|
||||
"_channel": {
|
||||
"__id__": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.TrackPath",
|
||||
"_paths": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 4
|
||||
},
|
||||
"spriteFrame"
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.HierarchyPath",
|
||||
"path": "Node"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.ComponentPath",
|
||||
"component": "cc.Sprite"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.animation.Channel",
|
||||
"_curve": {
|
||||
"__id__": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"__type__": "cc.ObjectCurve",
|
||||
"_times": [
|
||||
0,
|
||||
0.041666666666666664,
|
||||
0.08333333333333333,
|
||||
0.125,
|
||||
0.16666666666666666
|
||||
],
|
||||
"_values": [
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@c9de1",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@1ebb0",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@0e5a2",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
{
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@360bd",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"__type__": "cc.AnimationClipAdditiveSettings",
|
||||
"enabled": false,
|
||||
"refClip": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/anm/atk/atk05.anim.meta
Normal file
13
assets/resources/game/skill/anm/atk/atk05.anim.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "2.0.3",
|
||||
"importer": "animation-clip",
|
||||
"imported": true,
|
||||
"uuid": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
|
||||
"files": [
|
||||
".cconb"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"name": "atk05"
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk",
|
||||
"_name": "atk1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk",
|
||||
"_name": "atk1",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
@@ -108,8 +108,8 @@
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": -0.5,
|
||||
"y": 0.5,
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
@@ -136,8 +136,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 139,
|
||||
"height": 128
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -186,7 +186,7 @@
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "3d46f945-3f07-477e-a95a-b49557d552c6",
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
@@ -222,8 +222,8 @@
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 50
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
@@ -248,8 +248,13 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"distance": 100,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
@@ -315,8 +320,8 @@
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 30,
|
||||
"height": 50
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
@@ -336,7 +341,7 @@
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": false,
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "45017156-064b-43f2-a24e-176b6ce17ad8",
|
||||
@@ -8,6 +8,6 @@
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk"
|
||||
"syncNodeName": "atk1"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk2.prefab
Normal file
375
assets/resources/game/skill/atk/atk2.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk2",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk2",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "6b9c9f74-0168-4b57-824d-bfcc2cd1785d",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk2.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk2.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "ff6e9a75-b1b9-448e-9cbd-d30de98fd21b",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk2"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk3.prefab
Normal file
375
assets/resources/game/skill/atk/atk3.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk3",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk3",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "26398731-5541-44ca-b2b7-4947dc9bf7f7",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk3.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk3.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "16ca6f33-f910-4202-add4-2862be6c9127",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk3"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk4.prefab
Normal file
375
assets/resources/game/skill/atk/atk4.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk4",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk4",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "0ad94bf2-3b2d-4f80-8500-68c5e33e2ed1",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk4.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk4.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "7f30cd3c-57d1-476a-96bb-02c4eb082576",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk4"
|
||||
}
|
||||
}
|
||||
375
assets/resources/game/skill/atk/atk5.prefab
Normal file
375
assets/resources/game/skill/atk/atk5.prefab
Normal file
@@ -0,0 +1,375 @@
|
||||
[
|
||||
{
|
||||
"__type__": "cc.Prefab",
|
||||
"_name": "atk5",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_native": "",
|
||||
"data": {
|
||||
"__id__": 1
|
||||
},
|
||||
"optimizationPolicy": 0,
|
||||
"persistent": false
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "atk5",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": null,
|
||||
"_children": [
|
||||
{
|
||||
"__id__": 2
|
||||
}
|
||||
],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 8
|
||||
},
|
||||
{
|
||||
"__id__": 10
|
||||
},
|
||||
{
|
||||
"__id__": 12
|
||||
},
|
||||
{
|
||||
"__id__": 14
|
||||
},
|
||||
{
|
||||
"__id__": 16
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 18
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1,
|
||||
"y": 1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Node",
|
||||
"_name": "Node",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"_parent": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_children": [],
|
||||
"_active": true,
|
||||
"_components": [
|
||||
{
|
||||
"__id__": 3
|
||||
},
|
||||
{
|
||||
"__id__": 5
|
||||
}
|
||||
],
|
||||
"_prefab": {
|
||||
"__id__": 7
|
||||
},
|
||||
"_lpos": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_lrot": {
|
||||
"__type__": "cc.Quat",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0,
|
||||
"w": 1
|
||||
},
|
||||
"_lscale": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 1.3,
|
||||
"y": 1.1,
|
||||
"z": 1
|
||||
},
|
||||
"_mobility": 0,
|
||||
"_layer": 1073741824,
|
||||
"_euler": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 4
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 95,
|
||||
"height": 96
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "93pNmvtQlLSqtTgIepyEmA"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Sprite",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 2
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 6
|
||||
},
|
||||
"_customMaterial": null,
|
||||
"_srcBlendFactor": 2,
|
||||
"_dstBlendFactor": 4,
|
||||
"_color": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_spriteFrame": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20@3c745",
|
||||
"__expectedType__": "cc.SpriteFrame"
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_fillStart": 0,
|
||||
"_fillRange": 0,
|
||||
"_isTrimmedMode": true,
|
||||
"_useGrayscale": false,
|
||||
"_atlas": {
|
||||
"__uuid__": "2842dc10-d89e-4ff6-8a11-6d09bf48cf20",
|
||||
"__expectedType__": "cc.SpriteAtlas"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "12eKc8gltBz50frJCS5+ww"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "3arqAMBz1MvoXBzeDaL5M/",
|
||||
"instance": null,
|
||||
"targetOverrides": null,
|
||||
"nestedPrefabInstanceRoots": null
|
||||
},
|
||||
{
|
||||
"__type__": "cc.UITransform",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 9
|
||||
},
|
||||
"_contentSize": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_anchorPoint": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0.5,
|
||||
"y": 0.5
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "63NP9yq3hEUKD/OZZZ5t7x"
|
||||
},
|
||||
{
|
||||
"__type__": "57aabs7TE1J5obTAZczc+64",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 20,
|
||||
"atk_y": 40,
|
||||
"runType": 2,
|
||||
"endType": 0,
|
||||
"speed": 720,
|
||||
"time": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "35nW0iQNBH2bqkWAr3MVkQ"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.RigidBody2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 13
|
||||
},
|
||||
"enabledContactListener": true,
|
||||
"bullet": false,
|
||||
"awakeOnLoad": true,
|
||||
"_group": 1,
|
||||
"_type": 1,
|
||||
"_allowSleep": false,
|
||||
"_gravityScale": 1,
|
||||
"_linearDamping": 0,
|
||||
"_angularDamping": 0,
|
||||
"_linearVelocity": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_angularVelocity": 0,
|
||||
"_fixedRotation": false,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "57cr7S0YpK4oEZCavwXtIX"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.BoxCollider2D",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 15
|
||||
},
|
||||
"tag": 0,
|
||||
"_group": 1,
|
||||
"_density": 1,
|
||||
"_sensor": true,
|
||||
"_friction": 0.2,
|
||||
"_restitution": 0,
|
||||
"_offset": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"_size": {
|
||||
"__type__": "cc.Size",
|
||||
"width": 120,
|
||||
"height": 120
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "ebw5mObxNGGKxnJXDgX5gl"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.Animation",
|
||||
"_name": "",
|
||||
"_objFlags": 0,
|
||||
"__editorExtras__": {},
|
||||
"node": {
|
||||
"__id__": 1
|
||||
},
|
||||
"_enabled": true,
|
||||
"__prefab": {
|
||||
"__id__": 17
|
||||
},
|
||||
"playOnLoad": true,
|
||||
"_clips": [
|
||||
{
|
||||
"__uuid__": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
}
|
||||
],
|
||||
"_defaultClip": {
|
||||
"__uuid__": "ba0a95bf-2ea1-4d27-9c4f-cbd970ca558c",
|
||||
"__expectedType__": "cc.AnimationClip"
|
||||
},
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
"fileId": "20U0hP/NZErJJXRCEib1lq"
|
||||
},
|
||||
{
|
||||
"__type__": "cc.PrefabInfo",
|
||||
"root": {
|
||||
"__id__": 1
|
||||
},
|
||||
"asset": {
|
||||
"__id__": 0
|
||||
},
|
||||
"fileId": "c46/YsCPVOJYA4mWEpNYRx",
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
13
assets/resources/game/skill/atk/atk5.prefab.meta
Normal file
13
assets/resources/game/skill/atk/atk5.prefab.meta
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"ver": "1.1.50",
|
||||
"importer": "prefab",
|
||||
"imported": true,
|
||||
"uuid": "7227b42b-9cc7-41bd-8858-9d05c4cc758b",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {
|
||||
"syncNodeName": "atk5"
|
||||
}
|
||||
}
|
||||
@@ -282,7 +282,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 25,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -282,7 +282,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 25,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -282,7 +282,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 25,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -253,7 +253,11 @@
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -368,4 +372,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -389,7 +389,11 @@
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -504,4 +508,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -250,7 +250,11 @@
|
||||
},
|
||||
"atk_x": 10,
|
||||
"atk_y": 15,
|
||||
"_id": ""
|
||||
"_id": "",
|
||||
"runType": 1,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -336,4 +340,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 926 KiB After Width: | Height: | Size: 982 KiB |
File diff suppressed because it is too large
Load Diff
@@ -232,7 +232,7 @@
|
||||
"_top": 0,
|
||||
"_bottom": 546.004,
|
||||
"_horizontalCenter": 0,
|
||||
"_verticalCenter": 143,
|
||||
"_verticalCenter": -7,
|
||||
"_isAbsLeft": true,
|
||||
"_isAbsRight": true,
|
||||
"_isAbsTop": true,
|
||||
@@ -276,7 +276,7 @@
|
||||
"value": {
|
||||
"__type__": "cc.Vec3",
|
||||
"x": 0,
|
||||
"y": 783,
|
||||
"y": 633,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
|
||||
@@ -38,6 +38,23 @@ export interface CloudData {
|
||||
openid: string;
|
||||
data: GameDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 死亡英雄快照:英雄真实死亡时记录,供后续复活机制(重新召唤)消费。
|
||||
* 仅记录静态/成长数据,不含战斗内临时状态(buff、CD 等)。
|
||||
*/
|
||||
export interface DeadHeroRecord {
|
||||
/** 英雄模板 uuid(决定重新召唤哪个英雄) */
|
||||
uuid: number;
|
||||
/** 死亡时的英雄等级(复活后继承) */
|
||||
lv: number;
|
||||
/** 卡牌等级(驱动 bg_node 颜色) */
|
||||
card_lv: number;
|
||||
/** 死亡时占用的站位索引(复活时优先归位,-1 表示未分配) */
|
||||
posIndex: number;
|
||||
/** 死亡时的回合数(用于统计/按回合清理) */
|
||||
wave: number;
|
||||
}
|
||||
/** 游戏模块 */
|
||||
@ecs.register('SingletonModule')
|
||||
export class SingletonModuleComp extends ecs.Comp {
|
||||
@@ -58,8 +75,10 @@ export class SingletonModuleComp extends ecs.Comp {
|
||||
in_select: false,
|
||||
in_fight: false,
|
||||
stop_mon_action: false,
|
||||
heroGrid: [-1, -1, -1, -1, -1, -1],
|
||||
heroGrid: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
|
||||
monGrid: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
|
||||
/** 本局真实死亡的英雄快照列表,供后续复活机制消费;整局结束(MissionEnd)时清空 */
|
||||
dead_heroes: [] as DeadHeroRecord[],
|
||||
};
|
||||
data: any = {
|
||||
openid: '',
|
||||
|
||||
@@ -104,12 +104,12 @@ export const BuffList: Record<number, BuffConfig> = {
|
||||
modifiers: [{ attr: Attrs.critical, op: ModOp.Flat, value: 30 }],
|
||||
info: "暴击率提升 30%,持续 5 秒",
|
||||
},
|
||||
// 击退概率强化(限时)
|
||||
// 击退距离强化(限时)
|
||||
7013: {
|
||||
id: 7013, name: "击退爆发", icon: "Stat_Stun_01",
|
||||
category: BuffCategory.Buff, duration: 5, max_stack: 1,
|
||||
modifiers: [{ attr: Attrs.knockback_chance, op: ModOp.Flat, value: 30 }],
|
||||
info: "击退概率提升 30%,持续 5 秒",
|
||||
info: "击退距离提升 30,持续 5 秒",
|
||||
},
|
||||
// 穿透强化(限时)
|
||||
7014: {
|
||||
|
||||
@@ -46,7 +46,6 @@ export enum CardTriggerType {
|
||||
Interval = 2, // 定时循环:战斗中按 t_inv 间隔重复触发
|
||||
Field = 3, // 驻场光环(俗称「装备」):被动生效,由 field 数组驱动,支持单卡多词条
|
||||
FightStart = 4, // 战斗开始时触发
|
||||
FightEnd = 5, // 战斗结束时触发(每回合结束)
|
||||
HeroDead = 6, // 场上己方英雄死亡时触发
|
||||
HeroCall = 7, // 英雄上场时触发(主角召唤 + 技能召唤 + 复活)
|
||||
}
|
||||
@@ -78,7 +77,7 @@ export interface CardConfig {
|
||||
/** 触发类型(必填,技能卡专用;功能卡/英雄卡可缺省) */
|
||||
trigger_type?: CardTriggerType;
|
||||
/**
|
||||
* 事件型触发的全局次数上限(仅 FightStart/FightEnd/HeroDead/HeroCall 有效)
|
||||
* 事件型触发的全局次数上限(仅 FightStart/HeroDead/HeroCall 有效)
|
||||
* 默认 Infinity;达到上限后销毁节点
|
||||
* 注意:与 t_times 语义不同——t_times 控制每回合内 Interval 的次数
|
||||
*/
|
||||
|
||||
@@ -79,7 +79,6 @@ const EquipRawList: EquipRawData[] = [
|
||||
{ uuid: 8811, card_lv: 3, icon: "Wand-FA_WP_Main_Wand_004_WoodGold", name: "召唤强化", info: "召唤触发技能次数+1", field: [{ uuid: 7014, value: 1 }] },
|
||||
{ uuid: 8812, card_lv: 3, icon: "Staff-FA_WP_Main_Staff_005_WoodGold", name: "死亡强化", info: "死亡触发技能次数+1", field: [{ uuid: 7015, value: 1 }] },
|
||||
{ uuid: 8813, card_lv: 3, icon: "Spear-FA_WP_Main_Spear_008_GrayGold", name: "开场强化", info: "战斗开始触发技能次数+1", field: [{ uuid: 7016, value: 1 }] },
|
||||
{ uuid: 8814, card_lv: 3, icon: "Blunt-FA_WP_Main_Blunt_006_OrangeGray", name: "结束强化", info: "战斗结束触发技能次数+1", field: [{ uuid: 7017, value: 1 }] },
|
||||
{ uuid: 8815, card_lv: 3, icon: "Sword-FA_WP_Main_Sword_001_Gold", name: "攻击强化", info: "攻击触发技能次数+1", field: [{ uuid: 7018, value: 1 }] },
|
||||
{ uuid: 8816, card_lv: 3, icon: "Shield-FA_WP_Sub_Shield_009_RedGold", name: "受击强化", info: "被攻击触发技能次数+1", field: [{ uuid: 7019, value: 1 }] },
|
||||
// 多词条装备
|
||||
|
||||
@@ -36,7 +36,6 @@ export enum GameEvent {
|
||||
FightStart = "FightStart",
|
||||
FightPause = "FightPause",
|
||||
FightResume = "FightResume",
|
||||
FightEnd = "FightEnd",
|
||||
MissionEnd = "MissionEnd",
|
||||
MissionComplete = "MissionComplete",//战斗结算完成
|
||||
HeroSkillSelect = "HeroSkillSelect",
|
||||
|
||||
@@ -10,10 +10,10 @@ export enum BoxSet {
|
||||
MONSTER = 2,
|
||||
HERO = 4,
|
||||
//地图边界
|
||||
LETF_END = -360,
|
||||
RIGHT_END = 360,
|
||||
LETF_END = -320,
|
||||
RIGHT_END = 320,
|
||||
//游戏地平线
|
||||
GAME_LINE = 160,
|
||||
GAME_LINE = 10,
|
||||
/** 技能/装备各自独立的上限 */
|
||||
BOX_MAX = 6,
|
||||
}
|
||||
@@ -37,14 +37,14 @@ export enum FightSet {
|
||||
/** 英雄属性随等级成长的倍率(ap/hp = base * MULTIPLIER^(lv-1)) */
|
||||
HERO_LV_MULTIPLIER = 2,
|
||||
// BACK_RANG=30,//后退范围
|
||||
BACK_RANG = 30,//后退范围
|
||||
BACK_RANG = 20,//后退范围
|
||||
FiIGHT_TIME = 30,//战斗时间
|
||||
// BACK_CHANCE=40,//击退概率
|
||||
FROST_TIME = 3,//冰冻时间
|
||||
STUN_TIME = 2,//击晕时间
|
||||
SKILL_CAST_DELAY = 0.15,
|
||||
CSKILL_START_X = -340,
|
||||
CSKILL_START_Y = 30,
|
||||
/** 护盾数值上限(数值减免型:1 点护盾吸收 1 点伤害) */
|
||||
SHIELD_MAX = 5,
|
||||
WAVE_HEAL_RATE = 0.4, // 回合结束时所有英雄恢复最大生命值的比例(与 DDA 放水兜底分层:回血保节奏,放水防崩盘)
|
||||
PUNCTURE_DOWN = 50,
|
||||
|
||||
@@ -31,7 +31,7 @@ export enum Attrs {
|
||||
freeze_res = "freeze_res", // 冰冻抗性
|
||||
stun_chance = "stun_chance", // 击晕概率
|
||||
stun_res = "stun_res", // 击晕抗性
|
||||
knockback_chance = "knockback_chance", // 击退概率
|
||||
knockback_chance = "knockback_chance", // 击退强化(原击退概率,现转换为击退距离增量)
|
||||
knockback_distance = "knockback_distance", // 击退距离强化
|
||||
knockback_res = "knockback_res", // 击退抗性
|
||||
invincible_time = "invincible_time",// 无敌时间
|
||||
|
||||
@@ -45,7 +45,6 @@ export interface ISkillDescSource {
|
||||
call?: TriggerGrouped;
|
||||
dead?: TriggerGrouped;
|
||||
fstart?: TriggerGrouped;
|
||||
fend?: TriggerGrouped;
|
||||
atking?: TriggerGrouped;
|
||||
atked?: TriggerGrouped;
|
||||
field?: LvFieldEntry[];
|
||||
@@ -85,7 +84,6 @@ const TRIGGER_KEYS: SkillTriggerType[] = [
|
||||
SkillTriggerType.Call,
|
||||
SkillTriggerType.Dead,
|
||||
SkillTriggerType.FStart,
|
||||
SkillTriggerType.FEnd,
|
||||
SkillTriggerType.Atking,
|
||||
SkillTriggerType.Atked,
|
||||
];
|
||||
|
||||
@@ -38,7 +38,7 @@ const ItemCardData: ItemCardRaw[] = [
|
||||
// ==================== 计时性强化(统一 5s) ====================
|
||||
{ uuid: 9102, skill: 6503, icon: "Sub_Item-FA_WP_Sub_Potion_001_Red", name: "攻击药水", info: "全体友方攻击力提升", cost: 0, weight: 15 },
|
||||
{ uuid: 9103, skill: 6504, icon: "Sub_Item-FA_WP_Sub_Potion_001_Yellow", name: "暴击药水", info: "全体友方暴击率提升", cost: 0, weight: 15 },
|
||||
{ uuid: 9104, skill: 6505, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "击退药水", info: "全体友方击退概率提升", cost: 0, weight: 12 },
|
||||
{ uuid: 9104, skill: 6505, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "击退药水", info: "全体友方击退距离提升", cost: 0, weight: 12 },
|
||||
{ uuid: 9105, skill: 6506, icon: "Sub_Item-FA_WP_Sub_Potion_001_Blue", name: "穿透药水", info: "全体友方穿透概率提升", cost: 0, weight: 12 },
|
||||
{ uuid: 9106, skill: 6507, icon: "Sub_Item-FA_WP_Sub_Potion_001_Red", name: "攻速药水", info: "全体友方攻击速度提升", cost: 0, weight: 15 },
|
||||
{ uuid: 9107, skill: 6508, icon: "Sub_Item-FA_WP_Sub_Potion_001_Purple", name: "风怒药水", info: "全体友方风怒概率提升", cost: 0, weight: 12 },
|
||||
|
||||
@@ -221,7 +221,7 @@ export const SkillDescSet: Record<number, SkillDescConfig> = {
|
||||
templTimed: "{target}{verb}{value}{unit},持续{dur}秒",
|
||||
},
|
||||
6505: {
|
||||
verb: "击退概率提升", unit: "%", permField: "ap", timedField: "buff_value",
|
||||
verb: "击退距离提升", unit: "", permField: "ap", timedField: "buff_value",
|
||||
templPerm: "{target}{verb}{value}{unit}",
|
||||
templTimed: "{target}{verb}{value}{unit},持续{dur}秒",
|
||||
},
|
||||
@@ -287,7 +287,6 @@ export const FieldDescSet: Record<number, FieldDescConfig> = {
|
||||
[FieldSkillType.SummonCount]: { verb: "召唤触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.DeadCount]: { verb: "死亡触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.StartCount]: { verb: "战斗开始触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.EndCount]: { verb: "战斗结束触发技能次数", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.WaveGold]: { verb: "每回合金币收益", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.SellGold]: { verb: "卖出英雄金币", fmt: FieldValueFmt.Int },
|
||||
[FieldSkillType.WaveHeal]: { verb: "战斗结束生命回复量", fmt: FieldValueFmt.Percent },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// ========== 从 HeroAttrs.ts 导入属性相关定义 ==========
|
||||
// ========== 从 HeroAttrs.ts 导入属性相关定义 ==========
|
||||
import { Attrs } from "./HeroAttrs";
|
||||
|
||||
export enum HSSet {
|
||||
@@ -152,7 +152,7 @@ export interface SkillConfig {
|
||||
crt?: number, // 额外暴击率
|
||||
frz?: number, // 额外冰冻概率
|
||||
stun?: number, // 额外击晕概率
|
||||
bck?: number, // 额外击退概率
|
||||
bck?: number, // 额外击退强化(击退距离增量)
|
||||
buff_type?: Attrs, // Buff 类型 (单一职责)
|
||||
timed_buff_id?: number, // 计时性 buff 配置 id(指向 BuffList),存在时走 buffManager 而非永久 add_*
|
||||
buff_value?: number, // 覆写计时 buff 的修饰数值(modifiers.value / tick.damage_or_heal),由药水卡自定义档位
|
||||
@@ -218,28 +218,28 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6002: {
|
||||
6002: {
|
||||
uuid: 6002, name: "飞刀", sp_name: "a2", icon: "Sword-FA_WP_Main_Sword_009_GraySilver", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6003: {
|
||||
6003: {
|
||||
uuid: 6003, name: "匕首", sp_name: "a3", icon: "Sword-FA_WP_Main_Sword_019_WoodSilver", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6004: {
|
||||
6004: {
|
||||
uuid: 6004, name: "飞斧", sp_name: "a4", icon: "Axe-FA_WP_Main_Axe_004_WoodGray", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6005: {
|
||||
6005: {
|
||||
uuid: 6005, name: "飞枪", sp_name: "a5", icon: "Spear-FA_WP_Main_Spear_010_MetalBlue", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
6006: {
|
||||
uuid: 6006, name: "攻击", sp_name: "atk", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
uuid: 6006, name: "攻击", sp_name: "atk1", icon: "Stat_Attack_01", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.Melee, is_accel: true,
|
||||
RType: RType.bezier, EType: EType.collision, info: "造成攻击力100%的伤害",
|
||||
},
|
||||
@@ -363,7 +363,7 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
6406: {
|
||||
uuid: 6406, name: "击退强化", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 1, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退概率限时提升, 持续5秒",
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退距离限时提升, 持续5秒",
|
||||
},
|
||||
6407: {
|
||||
uuid: 6407, name: "距推强化", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
@@ -416,7 +416,7 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
6505: {
|
||||
uuid: 6505, name: "击退爆发", sp_name: "buff_wind", icon: "Stat_Stun_01", TGroup: TGroup.Team, readyAnm: "up_blue", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, kind: SkillKind.Support, ap: 0, hit_count: 3, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support,
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退概率提升30%, 持续5秒",
|
||||
RType: RType.fixed, EType: EType.animationEnd, buff_type: Attrs.knockback_chance, timed_buff_id: 7013, info: "全体友方击退距离提升30, 持续5秒",
|
||||
},
|
||||
6506: {
|
||||
uuid: 6506, name: "穿透爆发", sp_name: "buff_wind", icon: "Stat_Tripleshot", TGroup: TGroup.Team, readyAnm: "up_ap", endAnm: "", act: "atk",
|
||||
@@ -460,7 +460,6 @@ export enum FieldSkillType {
|
||||
SummonCount = 1, // 召唤触发技能次数提升
|
||||
DeadCount = 2, // 死亡触发技能次数提升
|
||||
StartCount = 3, // 战斗开始触发技能次数提升
|
||||
EndCount = 4, // 战斗结束触发技能次数提升
|
||||
WaveGold = 5, // 每回合金币收益提升
|
||||
SellGold = 6, // 卖出英雄金币提升
|
||||
WaveHeal = 7, // 战斗结束生命回复量提升
|
||||
@@ -515,7 +514,6 @@ export const FieldSkillSet: Record<number, FieldSkillConfig> = {
|
||||
7014: { uuid: 7014, name: "召唤强化", icon: "Stat_UnitSummonIncrease_02", type: FieldSkillType.SummonCount, info: "召唤触发技能次数提升" },
|
||||
7015: { uuid: 7015, name: "死亡强化", icon: "Stat_PoisonChanceIncrease", type: FieldSkillType.DeadCount, info: "死亡触发技能次数提升" },
|
||||
7016: { uuid: 7016, name: "开场强化", icon: "Stat_AttackRangeIncrease_01", type: FieldSkillType.StartCount, info: "战斗开始触发技能次数提升" },
|
||||
7017: { uuid: 7017, name: "结束强化", icon: "Stat_UnitSummonIncrease_01", type: FieldSkillType.EndCount, info: "战斗结束触发技能次数提升" },
|
||||
7018: { uuid: 7018, name: "攻击强化", icon: "Stat_Attack_03", type: FieldSkillType.AtkCount, info: "攻击触发技能次数提升" },
|
||||
7019: { uuid: 7019, name: "受击强化", icon: "Stat_Armor_01", type: FieldSkillType.BeAtkCount, info: "被攻击触发技能次数提升" },
|
||||
7020: { uuid: 7020, name: "穿透强化", icon: "Stat_Tripleshot", type: FieldSkillType.HeroPunctureDmg, info: "英雄穿透后伤害提升(百分点)" },
|
||||
|
||||
@@ -40,37 +40,22 @@ export const MonTypeName: Record<number, string> = {
|
||||
|
||||
/**
|
||||
* 英雄攻击速度分级配置 (数值为攻击间隔cd,越小越快)
|
||||
* 每档3个速度级别:Lv1(最快) → Lv2(中间) → Lv3(最慢)
|
||||
* 6档共18级,覆盖所有英雄/怪物的攻击间隔
|
||||
* 5 档速度:最快 VeryFast 0.75s → 最慢 VerySlow 1.50s
|
||||
*/
|
||||
export enum AtkSpeedLv {
|
||||
VeryFast1 = 1, VeryFast2 = 2, VeryFast3 = 3,
|
||||
Fast1 = 4, Fast2 = 5, Fast3 = 6,
|
||||
Normal1 = 7, Normal2 = 8, Normal3 = 9,
|
||||
Mid1 = 10, Mid2 = 11, Mid3 = 12,
|
||||
Slow1 = 13, Slow2 = 14, Slow3 = 15,
|
||||
VerySlow1 = 16, VerySlow2 = 17, VerySlow3 = 18,
|
||||
VeryFast = 1,
|
||||
Fast = 2,
|
||||
Normal = 3,
|
||||
Slow = 4,
|
||||
VerySlow = 5,
|
||||
}
|
||||
|
||||
export const AtkSpeedSet = {
|
||||
[AtkSpeedLv.VeryFast1]: { name: "极速++", cd: 0.15 },
|
||||
[AtkSpeedLv.VeryFast2]: { name: "极速+", cd: 0.20 },
|
||||
[AtkSpeedLv.VeryFast3]: { name: "极速", cd: 0.30 },
|
||||
[AtkSpeedLv.Fast1]: { name: "快速++", cd: 0.40 },
|
||||
[AtkSpeedLv.Fast2]: { name: "快速+", cd: 0.50 },
|
||||
[AtkSpeedLv.Fast3]: { name: "快速", cd: 0.70 },
|
||||
[AtkSpeedLv.Normal1]: { name: "中速++", cd: 0.80 },
|
||||
[AtkSpeedLv.Normal2]: { name: "中速+", cd: 0.90 },
|
||||
[AtkSpeedLv.Normal3]: { name: "中速", cd: 1.00 },
|
||||
[AtkSpeedLv.Mid1]: { name: "一般+", cd: 1.10 },
|
||||
[AtkSpeedLv.Mid2]: { name: "一般", cd: 1.20 },
|
||||
[AtkSpeedLv.Mid3]: { name: "一般-", cd: 1.30 },
|
||||
[AtkSpeedLv.Slow1]: { name: "慢", cd: 1.50 },
|
||||
[AtkSpeedLv.Slow2]: { name: "慢+", cd: 1.60 },
|
||||
[AtkSpeedLv.Slow3]: { name: "慢++", cd: 1.80 },
|
||||
[AtkSpeedLv.VerySlow1]: { name: "很慢", cd: 2.30 },
|
||||
[AtkSpeedLv.VerySlow2]: { name: "很慢+", cd: 2.50 },
|
||||
[AtkSpeedLv.VerySlow3]: { name: "很慢++", cd: 2.80 },
|
||||
[AtkSpeedLv.VeryFast]: { name: "极速", cd: 0.75 },
|
||||
[AtkSpeedLv.Fast]: { name: "快速", cd: 0.90 },
|
||||
[AtkSpeedLv.Normal]: { name: "中速", cd: 1.05 },
|
||||
[AtkSpeedLv.Slow]: { name: "慢速", cd: 1.20 },
|
||||
[AtkSpeedLv.VerySlow]: { name: "极慢", cd: 1.50 },
|
||||
};
|
||||
|
||||
export const HeroPos = {
|
||||
@@ -85,10 +70,11 @@ export const FormationPointX = {
|
||||
[HType.Long]: 100,
|
||||
} as const;
|
||||
|
||||
/** 各攻击定位的默认攻击距离(像素):近战100 / 中程300 / 远程450;英雄配置了 dis 时优先取配置值 */
|
||||
export const HeroDisVal: Record<HType.Melee | HType.Mid | HType.Long, number> = {
|
||||
[HType.Melee]: 120,
|
||||
[HType.Mid]: 720,
|
||||
[HType.Long]: 720,
|
||||
[HType.Melee]: 100,
|
||||
[HType.Mid]: 300,
|
||||
[HType.Long]: 450,
|
||||
}
|
||||
|
||||
export const resolveFormationTargetX = (fac: FacSet, type: HType): number => {
|
||||
@@ -178,7 +164,6 @@ export enum SkillTriggerType {
|
||||
Call = "call", // 召唤后触发 降临
|
||||
Dead = "dead", // 死亡后触发 遗志
|
||||
FStart = "fstart", // 战斗开始 先手
|
||||
FEnd = "fend", // 战斗结束 终战
|
||||
Field = "field", // 驻场 光环
|
||||
Atking = "atking", // 攻击后触发 追击
|
||||
Atked = "atked", // 受击后触发 反击
|
||||
@@ -189,7 +174,6 @@ export const SkillTriggerName = {
|
||||
[SkillTriggerType.Call]: "召唤",
|
||||
[SkillTriggerType.Dead]: "死亡后",
|
||||
[SkillTriggerType.FStart]: "回合开始",
|
||||
[SkillTriggerType.FEnd]: "回合结束",
|
||||
[SkillTriggerType.Field]: "光环",
|
||||
[SkillTriggerType.Atking]: "攻击",
|
||||
[SkillTriggerType.Atked]: "受伤",
|
||||
@@ -200,7 +184,6 @@ export const SkillTriggerDesc = {
|
||||
[SkillTriggerType.Call]: "召唤时",
|
||||
[SkillTriggerType.Dead]: "死亡时",
|
||||
[SkillTriggerType.FStart]: "战斗开始时",
|
||||
[SkillTriggerType.FEnd]: "战斗结束时",
|
||||
[SkillTriggerType.Field]: "光环",
|
||||
[SkillTriggerType.Atking]: "攻击n次",
|
||||
[SkillTriggerType.Atked]: "受伤n次",
|
||||
@@ -312,7 +295,6 @@ export interface heroInfo {
|
||||
[SkillTriggerType.Call]?: TriggerGrouped; // 召唤后触发的技能配置(按 s_uuid 分组,每组按 lv 升序)
|
||||
[SkillTriggerType.Dead]?: TriggerGrouped; // 死亡后触发的技能配置
|
||||
[SkillTriggerType.FStart]?: TriggerGrouped; // 战斗开始时释放的技能配置
|
||||
[SkillTriggerType.FEnd]?: TriggerGrouped; // 战斗结束时释放的技能配置
|
||||
[SkillTriggerType.Field]?: LvFieldEntry[]; // 驻场光环(按 lv 升序,运行时取 ≤英雄等级 的最大档)
|
||||
[SkillTriggerType.Atking]?: TriggerGrouped; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数
|
||||
[SkillTriggerType.Atked]?: TriggerGrouped; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数
|
||||
@@ -402,8 +384,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
|
||||
// 5001 铁壁战士 — atked 护盾(标杆,原5011)
|
||||
5001: {
|
||||
uuid: 5001, name: "铁壁战士", path: "hk1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 300, ap: 28,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5001, name: "铁壁战士", path: "hk1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 300, ap: 22,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
atked: {
|
||||
6301: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 2 } },
|
||||
@@ -415,8 +397,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5002 影袭刺客 — atking 自身攻击
|
||||
5002: {
|
||||
uuid: 5002, name: "影袭刺客", path: "hc1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 300, ap: 28,
|
||||
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } },
|
||||
uuid: 5002, name: "影袭刺客", path: "hc1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 300, ap: 22,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
|
||||
atking: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 1 } },
|
||||
@@ -428,8 +410,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5003 遗志将军 — dead 全队攻击
|
||||
5003: {
|
||||
uuid: 5003, name: "遗志将军", path: "hk2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 300, ap: 28,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5003, name: "遗志将军", path: "hk2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 300, ap: 22,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
dead: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
|
||||
@@ -441,8 +423,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5004 破晓先知 — fstart 全队攻击
|
||||
5004: {
|
||||
uuid: 5004, name: "破晓先知", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 143, ap: 40,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
|
||||
uuid: 5004, name: "破晓先知", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 143, ap: 32,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
|
||||
@@ -454,8 +436,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5005 战歌法师 — field 攻击光环
|
||||
5005: {
|
||||
uuid: 5005, name: "战歌法师", path: "hm2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 143, ap: 40,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
|
||||
uuid: 5005, name: "战歌法师", path: "hm2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 143, ap: 32,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7002, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7002, value: 0.2 }] },
|
||||
@@ -468,8 +450,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
|
||||
// 5101 不死斗士 — atked 回血(更高数值)
|
||||
5101: {
|
||||
uuid: 5101, name: "不死斗士", path: "hk3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 600, ap: 57,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5101, name: "不死斗士", path: "hk3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 600, ap: 46,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
atked: {
|
||||
6302: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 200 } },
|
||||
@@ -481,8 +463,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5102 致命杀手 — atking 暴击(更高数值)
|
||||
5102: {
|
||||
uuid: 5102, name: "致命杀手", path: "hc2", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 600, ap: 57,
|
||||
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast2].cd, ccd: 0 } },
|
||||
uuid: 5102, name: "致命杀手", path: "hc2", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 600, ap: 46,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
|
||||
atking: {
|
||||
6403: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
|
||||
@@ -494,8 +476,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5103 殉道勇士 — dead 全队护盾(更高数值)
|
||||
5103: {
|
||||
uuid: 5103, name: "殉道勇士", path: "hk4", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 600, ap: 57,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5103, name: "殉道勇士", path: "hk4", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 600, ap: 46,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
dead: {
|
||||
6301: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
|
||||
@@ -507,8 +489,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5104 圣光祭司 — fstart 全队护盾(更高数值)
|
||||
5104: {
|
||||
uuid: 5104, name: "圣光祭司", path: "hm3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 287, ap: 80,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow1].cd, ccd: 0 } },
|
||||
uuid: 5104, name: "圣光祭司", path: "hm3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 287, ap: 64,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6301: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
|
||||
@@ -520,8 +502,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5105 风怒先知 — field 攻速光环
|
||||
5105: {
|
||||
uuid: 5105, name: "风怒先知", path: "hm1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 287, ap: 80,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
|
||||
uuid: 5105, name: "风怒先知", path: "hm1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 287, ap: 64,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7006, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7006, value: 0.2 }] },
|
||||
@@ -531,8 +513,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5106 破甲射手 — atking 自身穿透伤害(计时buff增强)
|
||||
5106: {
|
||||
uuid: 5106, name: "破甲射手", path: "ha1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 287, ap: 80,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
|
||||
uuid: 5106, name: "破甲射手", path: "ha1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 287, ap: 64,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
|
||||
atking: {
|
||||
6512: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, timed_buff_id: 7022, buff_value: 5, buff_duration: 5 } },
|
||||
@@ -547,8 +529,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
|
||||
// 5201 怒血勇士 — atked 全队攻击(范围扩大)
|
||||
5201: {
|
||||
uuid: 5201, name: "怒血勇士", path: "hk5", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 900, ap: 85,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5201, name: "怒血勇士", path: "hk5", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 900, ap: 68,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
atked: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 1 } },
|
||||
@@ -560,8 +542,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5202 援护射手 — atking 队友攻击(范围扩大)
|
||||
5202: {
|
||||
uuid: 5202, name: "援护射手", path: "ha1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 430, ap: 120,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } },
|
||||
uuid: 5202, name: "援护射手", path: "ha1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 430, ap: 96,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
|
||||
atking: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 1, ap: 1 } },
|
||||
@@ -573,8 +555,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5203 传承卫士 — dead 全队生命(范围扩大)
|
||||
5203: {
|
||||
uuid: 5203, name: "传承卫士", path: "hk1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 900, ap: 85,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5203, name: "传承卫士", path: "hk1", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 900, ap: 68,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
dead: {
|
||||
6402: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 10 } },
|
||||
@@ -586,8 +568,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5204 晨光贤者 — fstart 全队暴击(范围扩大)
|
||||
5204: {
|
||||
uuid: 5204, name: "晨光贤者", path: "hm2", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 430, ap: 120,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid3].cd, ccd: 0 } },
|
||||
uuid: 5204, name: "晨光贤者", path: "hm2", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 430, ap: 96,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6403: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
|
||||
@@ -599,8 +581,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5205 精准术士 — field 暴击光环(范围扩大)
|
||||
5205: {
|
||||
uuid: 5205, name: "精准术士", path: "hm3", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 430, ap: 120,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
|
||||
uuid: 5205, name: "精准术士", path: "hm3", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 430, ap: 96,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7004, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7004, value: 0.2 }] },
|
||||
@@ -613,8 +595,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
|
||||
// 5301 复仇守护者 — atked 全队暴击(范围+数值)
|
||||
5301: {
|
||||
uuid: 5301, name: "复仇守护者", path: "hk2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1200, ap: 113,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow1].cd, ccd: 0 } },
|
||||
uuid: 5301, name: "复仇守护者", path: "hk2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1200, ap: 90,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
atked: {
|
||||
6403: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
|
||||
@@ -626,8 +608,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5302 破灭游侠 — atking 队友暴伤(范围+数值)
|
||||
5302: {
|
||||
uuid: 5302, name: "破灭游侠", path: "ha2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 573, ap: 160,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal1].cd, ccd: 0 } },
|
||||
uuid: 5302, name: "破灭游侠", path: "ha2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 573, ap: 128,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
|
||||
atking: {
|
||||
6404: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, hit_count: 2, timed_buff_id: 7020, buff_value: 5, buff_duration: 5 } },
|
||||
@@ -639,8 +621,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5303 亡语刺客 — dead 全队暴击+暴伤(双效果)
|
||||
5303: {
|
||||
uuid: 5303, name: "亡语刺客", path: "hc3", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 1200, ap: 113,
|
||||
skills: { 6003: { uuid: 6003, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } },
|
||||
uuid: 5303, name: "亡语刺客", path: "hc3", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Melee, grow_type: HGrowType.Assassin, role: HRole.Assassin, hp: 1200, ap: 90,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
|
||||
dead: {
|
||||
6403: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
|
||||
@@ -657,8 +639,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5304 战吼术士 — fstart 全队攻击+生命(双效果)
|
||||
5304: {
|
||||
uuid: 5304, name: "战吼术士", path: "hm1", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 573, ap: 160,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid1].cd, ccd: 0 } },
|
||||
uuid: 5304, name: "战吼术士", path: "hm1", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 573, ap: 128,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
|
||||
@@ -675,8 +657,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5305 领域贤者 — field 暴伤+攻速光环(双效果)
|
||||
5305: {
|
||||
uuid: 5305, name: "领域贤者", path: "hm2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 573, ap: 160,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Mid2].cd, ccd: 0 } },
|
||||
uuid: 5305, name: "领域贤者", path: "hm2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 573, ap: 128,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7005, value: 0.2 }, { uuid: 7006, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7005, value: 0.4 }, { uuid: 7006, value: 0.2 }] },
|
||||
@@ -689,8 +671,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
|
||||
// 5401 不灭战魂 — atked 攻击 + revive(双技能)
|
||||
5401: {
|
||||
uuid: 5401, name: "不灭战魂", path: "hk3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1500, ap: 142,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5401, name: "不灭战魂", path: "hk3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: 1500, ap: 114,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
atked: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 2 } },
|
||||
@@ -707,8 +689,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5402 战歌游侠 — atking 攻击 + field 攻速光环(双技能)
|
||||
5402: {
|
||||
uuid: 5402, name: "战歌游侠", path: "ha3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 717, ap: 200,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast3].cd, ccd: 0 } },
|
||||
uuid: 5402, name: "战歌游侠", path: "ha3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Assassin, role: HRole.Archer, hp: 717, ap: 160,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Fast].cd, ccd: 0 } },
|
||||
atking: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Self, ap: 2 } },
|
||||
@@ -725,8 +707,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5403 凤凰祭司 — dead 攻击+护盾 + revive(双技能)
|
||||
5403: {
|
||||
uuid: 5403, name: "凤凰祭司", path: "hk4", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 1500, ap: 142,
|
||||
skills: { 6001: { uuid: 6001, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow3].cd, ccd: 0 } },
|
||||
uuid: 5403, name: "凤凰祭司", path: "hk4", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Melee, grow_type: HGrowType.Warrior, role: HRole.Warrior, hp: 1500, ap: 114,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
dead: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
|
||||
@@ -748,8 +730,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5404 涅槃贤者 — fstart 护盾+攻击 + revive(双技能)
|
||||
5404: {
|
||||
uuid: 5404, name: "涅槃贤者", path: "hm3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 717, ap: 200,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal3].cd, ccd: 0 } },
|
||||
uuid: 5404, name: "涅槃贤者", path: "hm3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 717, ap: 160,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6301: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
|
||||
@@ -771,8 +753,8 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
// 5405 圣言诗人 — atking 治疗 + field 生命光环(双技能)
|
||||
5405: {
|
||||
uuid: 5405, name: "圣言诗人", path: "hh1", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Support, role: HRole.Support, hp: 717, ap: 200,
|
||||
skills: { 6015: { uuid: 6015, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal2].cd, ccd: 0 } },
|
||||
uuid: 5405, name: "圣言诗人", path: "hh1", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Support, role: HRole.Support, hp: 717, ap: 160,
|
||||
skills: { 6015: { uuid: 6015, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } },
|
||||
atking: {
|
||||
6302: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 5, overrides: { TGroup: TGroup.Team, ap: 200 } },
|
||||
@@ -789,76 +771,77 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
},
|
||||
|
||||
/*
|
||||
*=============怪物配置列表================
|
||||
* 基础近战型(lv:1) : SPEED:800 |AP:12 | HP:360 | skills[0].cd=0.65
|
||||
* 重型坦克型(lv:1) : SPEED:800 |AP:30 | HP:1050 | skills[0].cd=2
|
||||
* 远程dps(lv:1) : SPEED:800 |AP:45 | HP:240 | skills[0].cd=1.5
|
||||
* 远程辅助(lv:1) : SPEED:800 |AP:20 | HP:240 | skills[0].cd=1
|
||||
* 精英 (lv:1) : SPEED:800 |AP:20 | HP:4500 | skills[0].cd=1
|
||||
*=============怪物配置列表================
|
||||
* 性能基准:1 级怪物与 1 级英雄(card_lv=1)单体性能持平 ——
|
||||
* 近战怪镜像 5001(hp300/ap22/cd1.20),远程怪镜像 5004(hp160/ap28/cd1.20),
|
||||
* 重型/Boss 以更高 hp 换取更低 DPS,承担"墙"与"压场"定位。
|
||||
* 运行时最终属性 = 本表基础值 × RogueConfig.MON_HP_MUL / MON_AP_MUL
|
||||
* (Boss 额外 × BOSS_HP_BONUS_MUL),校准目标:整场战斗双方对砍 ≈ 30s。
|
||||
*/
|
||||
|
||||
|
||||
// 基础怪物 (全部固定点位站桩攻击,HType仅决定是前排还是后排)
|
||||
// 前排怪物 (站在前排,承受更多伤害) — v5: TD节奏CD,多而弱爽感设计
|
||||
// 前排怪物 — 近战镜像 1 级英雄基准(hp300/ap22/cd1.20),变体微分定位
|
||||
6001: {
|
||||
uuid: 6001, name: "兽人战士", path: "m1", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 220, ap: 10, speed: 70,
|
||||
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "基础前排怪"
|
||||
uuid: 6001, name: "兽人战士", path: "m1", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 300, ap: 22, speed: 70,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "基础前排怪, 与1级近战英雄持平"
|
||||
},
|
||||
6002: {
|
||||
uuid: 6002, name: "兽人精锐战士", path: "m2", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 300, ap: 14, speed: 110,
|
||||
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "进阶前排怪, 更快更痛"
|
||||
uuid: 6002, name: "兽人精锐战士", path: "m2", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Melee, hp: 340, ap: 26, speed: 110,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "进阶前排怪, 更快更痛"
|
||||
},
|
||||
6003: {
|
||||
uuid: 6003, name: "兽人重装兵", path: "m3", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Heavy, hp: 850, ap: 20, speed: 50,
|
||||
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "重型坦克怪, 高HP慢攻"
|
||||
uuid: 6003, name: "兽人重装兵", path: "m3", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.Heavy, hp: 800, ap: 14, speed: 50,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "重型坦克怪, 高HP低攻承伤位"
|
||||
},
|
||||
// 后排怪物 (站在后排,输出更高)
|
||||
// 后排怪物 — 远程镜像 1 级法师基准(hp160/ap28/cd1.20),变体微分定位
|
||||
6004: {
|
||||
uuid: 6004, name: "兽人射手", path: "m4", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Long, hp: 190, ap: 35, speed: 70,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排高DPS怪"
|
||||
uuid: 6004, name: "兽人射手", path: "m4", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Long, hp: 160, ap: 28, speed: 70,
|
||||
skills: { 6008: { uuid: 6008, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排输出怪, 与1级远程英雄持平"
|
||||
},
|
||||
6005: {
|
||||
uuid: 6005, name: "兽人刺客", path: "m5", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Assassin, hp: 210, ap: 38, speed: 130,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "高AP快速攻击刺客"
|
||||
uuid: 6005, name: "兽人刺客", path: "m5", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Assassin, hp: 130, ap: 26, speed: 130,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Normal].cd, ccd: 0 } }, info: "高攻速低HP刺客"
|
||||
},
|
||||
// 特殊位怪物
|
||||
6006: {
|
||||
uuid: 6006, name: "骷髅领主", path: "m6", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.MeleeBoss, hp: 5000, ap: 20, speed: 60,
|
||||
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排MiniBoss级坦克"
|
||||
uuid: 6006, name: "骷髅领主", path: "m6", fac: FacSet.MON, lv: 1, type: HType.Melee, monType: MonType.MeleeBoss, hp: 900, ap: 20, speed: 60,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "前排MiniBoss级坦克"
|
||||
},
|
||||
6007: {
|
||||
uuid: 6007, name: "兽人术士", path: "m7", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Support, hp: 300, ap: 24, speed: 70,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排法师怪, 魔法攻击"
|
||||
uuid: 6007, name: "兽人术士", path: "m7", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Support, hp: 240, ap: 18, speed: 70,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排辅助法师怪"
|
||||
},
|
||||
6008: {
|
||||
uuid: 6008, name: "兽人火法", path: "m8", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Summoner, hp: 270, ap: 32, speed: 70,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "后排高输出法师怪"
|
||||
uuid: 6008, name: "兽人火法", path: "m8", fac: FacSet.MON, lv: 1, type: HType.Long, monType: MonType.Summoner, hp: 220, ap: 24, speed: 70,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排召唤系法师怪"
|
||||
},
|
||||
|
||||
// BOSS怪物 — Boss节奏1.2-1.5s,删除不存在的6206技能
|
||||
// BOSS怪物 — 本表 hp/ap 仅作外观/技能档位参考,运行时面板由 RogueConfig 的
|
||||
// BOSS_BASE_HP / BOSS_BASE_AP(初始英雄 2 倍强度)统一决定,不读取本表数值
|
||||
6101: {
|
||||
uuid: 6101, name: "兽人首领-双刀战士", path: "mb1", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1900, ap: 30, speed: 120,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排Boss, 高攻速"
|
||||
uuid: 6101, name: "兽人首领-双刀战士", path: "mb1", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1200, ap: 24, speed: 120,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "前排Boss, 攻速型"
|
||||
},
|
||||
6102: {
|
||||
uuid: 6102, name: "兽人首领-斧头战士", path: "mb2", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 7500, ap: 26, speed: 60,
|
||||
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "前排Boss, 超高HP"
|
||||
uuid: 6102, name: "兽人首领-斧头战士", path: "mb2", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1500, ap: 18, speed: 60,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "前排Boss, 超高HP"
|
||||
},
|
||||
6103: {
|
||||
uuid: 6103, name: "兽人首领-魔法师", path: "mb3", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 2250, ap: 38, speed: 110,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow2].cd, ccd: 0 } }, info: "后排法系Boss, 高AP"
|
||||
uuid: 6103, name: "兽人首领-魔法师", path: "mb3", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 1000, ap: 28, speed: 110,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排法系Boss"
|
||||
},
|
||||
6104: {
|
||||
uuid: 6104, name: "兽人首领-射手", path: "mb4", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 6800, ap: 30, speed: 70,
|
||||
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "后排位Boss, 均衡型"
|
||||
uuid: 6104, name: "兽人首领-射手", path: "mb4", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 1100, ap: 24, speed: 70,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排位Boss, 均衡型"
|
||||
},
|
||||
6105: {
|
||||
uuid: 6105, name: "亡灵首领-法师", path: "mb5", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 2600, ap: 42, speed: 110,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow1].cd, ccd: 0 } }, info: "后排高AP Boss"
|
||||
uuid: 6105, name: "亡灵首领-法师", path: "mb5", fac: FacSet.MON, lv: 6, type: HType.Long, monType: MonType.LongBoss, hp: 1000, ap: 30, speed: 110,
|
||||
skills: { 6103: { uuid: 6103, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } }, info: "后排高AP Boss"
|
||||
},
|
||||
6106: {
|
||||
uuid: 6106, name: "亡灵首领-骑马战士", path: "mb6", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 9000, ap: 26, speed: 130,
|
||||
skills: { 6005: { uuid: 6005, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow3].cd, ccd: 0 } }, info: "前排终极Boss, 最高HP+高速"
|
||||
uuid: 6106, name: "亡灵首领-骑马战士", path: "mb6", fac: FacSet.MON, lv: 6, type: HType.Melee, monType: MonType.MeleeBoss, hp: 1800, ap: 20, speed: 130,
|
||||
skills: { 6006: { uuid: 6006, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.VerySlow].cd, ccd: 0 } }, info: "前排终极Boss, 最高HP+高速"
|
||||
},
|
||||
|
||||
|
||||
@@ -951,7 +934,7 @@ export const HeroComboList: number[] = [8001, 8002, 8003, 8004, 8005, 8006];
|
||||
* - W_HP: 每 1 点生命值折算 1 分(生命值不随时间变化)
|
||||
* - W_DPS_PER_SEC: 每 1 点 DPS 持续 1 秒折算 0.5 分
|
||||
* (默认 60 秒评估窗下,1 DPS ≈ 30 分,与旧版 W_DPS=30 等效)
|
||||
* - WAVE_DURATION / HITS_PER_WAVE: 回合参数,用于估算 atked/fstart/fend/dead 触发次数
|
||||
* - WAVE_DURATION / HITS_PER_WAVE: 回合参数,用于估算 atked/fstart/dead 触发次数
|
||||
* - DEFAULT_EVAL_SECONDS: calcHeroPower 默认使用的评估时间窗
|
||||
*/
|
||||
export const HERO_POWER_WEIGHTS = {
|
||||
@@ -960,7 +943,6 @@ export const HERO_POWER_WEIGHTS = {
|
||||
W_atking: 1.0,
|
||||
W_atked: 0.8,
|
||||
W_fstart: 1.0,
|
||||
W_fend: 1.2,
|
||||
W_dead: 0.6,
|
||||
W_field_alive: 1.0,
|
||||
W_revive: 1.0,
|
||||
@@ -1084,7 +1066,6 @@ function getTriggerCount(
|
||||
case SkillTriggerType.Atked:
|
||||
return getHitCount(seconds) / t_num;
|
||||
case SkillTriggerType.FStart:
|
||||
case SkillTriggerType.FEnd:
|
||||
return getWaveCount(seconds);
|
||||
case SkillTriggerType.Dead:
|
||||
return getWaveCount(seconds) * (1 + (resolveReviveByLv(hero.revive, FightSet.HERO_MAX_LV)?.r_num || 0));
|
||||
@@ -1160,7 +1141,9 @@ function calcSupportEffectValue(config: SkillConfig, apEff: number, cd: number,
|
||||
return (value / 100) * (apEff / cd) * 0.05 * weights.W_DPS_PER_SEC * seconds;
|
||||
case Attrs.stun_chance:
|
||||
case Attrs.freeze_chance:
|
||||
return value * weights.W_control;
|
||||
case Attrs.knockback_chance:
|
||||
// 击退强化已改为击退距离增量,按距离价值折算
|
||||
return value * weights.W_control;
|
||||
case Attrs.speed:
|
||||
return (value / 100) * (apEff / cd) * weights.W_DPS_PER_SEC * seconds;
|
||||
@@ -1182,7 +1165,8 @@ function calcSkillEffectValue(skill: SkillConfig, overrides: SkillOverrides | un
|
||||
case SkillKind.Heal:
|
||||
return ((config.ap || 0) / 100) * apEff * weights.W_heal;
|
||||
case SkillKind.Shield:
|
||||
return (config.ap || 0) * weights.expectedEnemyDmg * weights.W_shield;
|
||||
// 护盾为数值减免型:1 点护盾 ≈ 1 点预期伤害减免
|
||||
return (config.ap || 0) * weights.W_shield;
|
||||
case SkillKind.Support:
|
||||
return calcSupportEffectValue(config, apEff, cd, seconds);
|
||||
default:
|
||||
@@ -1213,7 +1197,6 @@ export interface HeroPowerBreakdown {
|
||||
atking: number;
|
||||
atked: number;
|
||||
fstart: number;
|
||||
fend: number;
|
||||
dead: number;
|
||||
field: number;
|
||||
revive: number;
|
||||
@@ -1228,7 +1211,7 @@ export interface HeroPowerDetailed {
|
||||
normalAttack: { s_uuid: number, name: string, cd: number, dps: number, value: number };
|
||||
}
|
||||
|
||||
/** 计算普通触发类技能(atking/atked/fstart/fend/dead)的详细价值 */
|
||||
/** 计算普通触发类技能(atking/atked/fstart/dead)的详细价值 */
|
||||
function calcTriggerPowerDetailed(
|
||||
triggerConfigs: { s_uuid: number, t_num?: number, overrides?: SkillOverrides }[],
|
||||
hero: heroInfo,
|
||||
@@ -1262,9 +1245,6 @@ function calcTriggerPowerDetailed(
|
||||
case SkillTriggerType.FStart:
|
||||
weight = weights.W_fstart;
|
||||
break;
|
||||
case SkillTriggerType.FEnd:
|
||||
weight = weights.W_fend;
|
||||
break;
|
||||
case SkillTriggerType.Dead:
|
||||
weight = weights.W_dead;
|
||||
break;
|
||||
@@ -1361,7 +1341,6 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
|
||||
const atking = hero.atking ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atking, targetLv), hero, targetLv, SkillTriggerType.Atking, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||
const atked = hero.atked ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atked, targetLv), hero, targetLv, SkillTriggerType.Atked, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||
const fstart = hero.fstart ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fstart, targetLv), hero, targetLv, SkillTriggerType.FStart, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||
const fend = hero.fend ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fend, targetLv), hero, targetLv, SkillTriggerType.FEnd, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||
const dead = hero.dead ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.dead, targetLv), hero, targetLv, SkillTriggerType.Dead, seconds) : { total: 0, items: [] as TriggerSkillEval[] };
|
||||
const fieldEntries = resolveFieldByLv(hero.field, targetLv);
|
||||
const field = fieldEntries.length > 0 ? calcFieldPowerDetailed(fieldEntries, hero, targetLv, seconds) : { total: 0, items: [] as FieldSkillEval[] };
|
||||
@@ -1372,7 +1351,7 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
|
||||
reviveValue = hpEff * revive.upr * revive.r_num * weights.W_revive * getWaveCount(seconds);
|
||||
}
|
||||
|
||||
const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + fend.total + dead.total + field.total + reviveValue;
|
||||
const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + dead.total + field.total + reviveValue;
|
||||
|
||||
return {
|
||||
total,
|
||||
@@ -1382,12 +1361,11 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco
|
||||
atking: atking.total,
|
||||
atked: atked.total,
|
||||
fstart: fstart.total,
|
||||
fend: fend.total,
|
||||
dead: dead.total,
|
||||
field: field.total,
|
||||
revive: reviveValue,
|
||||
},
|
||||
triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...fend.items, ...dead.items],
|
||||
triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...dead.items],
|
||||
fieldSkills: field.items,
|
||||
normalAttack,
|
||||
};
|
||||
|
||||
361
assets/script/game/common/config/几何王国技能复刻方案.md
Normal file
361
assets/script/game/common/config/几何王国技能复刻方案.md
Normal file
@@ -0,0 +1,361 @@
|
||||
# 几何王国技能复刻设计文档
|
||||
|
||||
> **目的**:基于《几何王国》热门英雄技能,分析哪些可以**零代码改动**或**最小改动**复刻到本系统(1 回合定生死机制)。
|
||||
>
|
||||
> **前置约束**(已确认的设计原则):
|
||||
> - 局内固定 lv=1,等级成长移交局外
|
||||
> - 单场战斗 30 秒
|
||||
> - card_lv 纯技能数量差异(1~5 个技能槽)
|
||||
> - 技能触发极简(`t_num=1`),高频高数值
|
||||
> - 无相同能力英雄,无阵容限制
|
||||
|
||||
---
|
||||
|
||||
## 一、本系统可复用的"触发骨架"
|
||||
|
||||
| 触发类型 | 枚举 | 语义 | 几何王国对应场景 |
|
||||
|---|---|---|---|
|
||||
| 战斗开始 | `SkillTriggerType.FStart` | 入场时触发 1 次 | 战吼、入场护盾 |
|
||||
| 战斗结束 | `SkillTriggerType.FEnd` | 战斗胜利后触发 | (30s 短局意义弱) |
|
||||
| 召唤入场 | `SkillTriggerType.Call` | 落地触发 1 次 | 登场技 |
|
||||
| 死亡触发 | `SkillTriggerType.Dead` | 死亡时触发 1 次 | 亡语、遗志 |
|
||||
| 攻击触发 | `SkillTriggerType.Atking` | 普攻 N 次触发 | 概率普攻特效(t_num 模拟概率) |
|
||||
| 受击触发 | `SkillTriggerType.Atked` | 被击 N 次触发 | 受击反击、叠甲 |
|
||||
| 驻场光环 | `SkillTriggerType.Field` | 存活期间全局生效 | 光环类(移速/攻击/回血) |
|
||||
| 复活 | `SkillTriggerType.Revive` | 死亡后复活 | 凤凰涅槃、尸骸存活 |
|
||||
|
||||
> **概率模拟技巧**:本系统无"概率触发"概念,用 `t_num` 计数模拟。
|
||||
> - `t_num=1`:每次普攻都触发(最高频)
|
||||
> - `t_num=3`:≈33% 概率
|
||||
> - `t_num=4`:≈25% 概率(对应几何王国的"30% 概率")
|
||||
|
||||
---
|
||||
|
||||
## 二、本系统可复用的"效果骨架"
|
||||
|
||||
### 2.1 SkillKind(技能主效果)
|
||||
|
||||
| 类型 | 字段 | 几何王国对应 |
|
||||
|---|---|---|
|
||||
| `SkillKind.Damage` | `ap` 百分比伤害 | 大招、战技伤害 |
|
||||
| `SkillKind.Heal` | `ap` 治疗量 | 治疗、吸血 |
|
||||
| `SkillKind.Shield` | `ap` 护盾层数(数值减免) | 暗光之盾、守护壁垒 |
|
||||
| `SkillKind.Support` | `buff_type` + `timed_buff_id` | 属性强化、限时增益 |
|
||||
| `SkillKind.Gold` | `gold` 金币数 | (本局不适用) |
|
||||
|
||||
### 2.2 击杀特效字段
|
||||
|
||||
| 字段 | 含义 | 几何王国对应 |
|
||||
|---|---|---|
|
||||
| `crt` | 额外暴击率 | 致命一击 |
|
||||
| `frz` | 额外冰冻概率 | 寒冰攻击 |
|
||||
| `stun` | 额外击晕概率 | 眩晕 |
|
||||
| `bck` | 击退距离增量 | 击退(雷霆一击、剑刃风暴) |
|
||||
| `hit_count` | 命中目标数 | 多段攻击(乱棍、剑刃风暴) |
|
||||
| `puncture_chance` | 穿透概率 | 穿透(暗器大师) |
|
||||
|
||||
### 2.3 BuffList 已有 DoT / HoT / 控制
|
||||
|
||||
| Buff ID | 名称 | 类型 | 几何王国对应 |
|
||||
|---|---|---|---|
|
||||
| 7002 | 灼烧 | Debuff DoT(-10 伤害/0.5s,吃 30% 攻击加成) | 剧毒利刃、火系持续伤害 |
|
||||
| 7003 | 冰冻 | Control(2s) | 冰冻 |
|
||||
| 7004 | 击晕 | Control(2s) | 眩晕、沉默(替代) |
|
||||
| 7010 | 再生 | Buff HoT(+10 治疗/0.5s) | 持续回血 |
|
||||
| 7011 | 攻击爆发 | Buff(+50% 攻击 5s) | 大圣归来、剑圣 1 觉 |
|
||||
| 7012 | 暴击爆发 | Buff(+30% 暴击 5s) | 暴击强化 |
|
||||
| 7017 | 击晕爆发 | Buff(+30% 击晕 5s) | 群体眩晕强化 |
|
||||
| 7111 | 战前强攻 | Buff(+30 攻击 5s) | 开场增益 |
|
||||
|
||||
### 2.4 FieldSkillType 已有光环
|
||||
|
||||
| 类型 | 几何王国对应 |
|
||||
|---|---|
|
||||
| `HeroAtk` | 攻击光环(人鱼公主、邪恶光环) |
|
||||
| `HeroCrit` / `HeroCritDamage` | 暴击光环 |
|
||||
| `HeroSpeed` | 攻速光环 |
|
||||
| `HeroHp` | 生命光环 |
|
||||
| `HeroStun` / `HeroWindFury` / `HeroPuncture` | 特殊光环 |
|
||||
| `WaveHeal` | 战后回血(30s 短局可改为局内 HoT) |
|
||||
|
||||
---
|
||||
|
||||
## 三、可直接复刻(零代码改动)
|
||||
|
||||
> 仅通过 `heroSet.ts` 配置 + 复用现有 SkillSet / BuffList 即可实现。
|
||||
|
||||
### 3.1 剧毒女王(Mage,复刻"深渊女王·剧毒利刃")
|
||||
|
||||
**核心机制**:普攻触发灼烧 DoT,叠加毒伤害。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 每次普攻上灼烧 DoT(已存在 BuffList 7002)
|
||||
6101: [{ lv: 1, t_num: 1, overrides: { timed_buff_id: 7002, buff_duration: 5 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:深渊女王·剧毒利刃(普攻 30% 概率 200% 法伤 + 减益)。
|
||||
**适配调整**:30s 战斗节奏快,`t_num=1` 100% 触发,但单层 DoT 数值调低以平衡。
|
||||
|
||||
---
|
||||
|
||||
### 3.2 猴王·乱棍(Assassin,复刻"至尊宝·大圣归来")
|
||||
|
||||
**核心机制**:普攻触发多段伤害+击晕。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 每次普攻触发 4 段 30% 伤害 + 高击晕
|
||||
6006: [{ lv: 1, t_num: 1, overrides: { ap: 30, hit_count: 4, stun: 30 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:至尊宝·大圣归来(乱棍 4 次 ×30% 物伤 + 眩晕)。
|
||||
**适配调整**:去除"召唤虚影+攻击+25%"(与本系统能量体系冲突),保留多段+击晕核心。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 剑圣·疾风(Warrior,复刻"剑圣·剑刃风暴+吸血面罩")
|
||||
|
||||
**核心机制**:高伤范围 + 击退 + 普攻吸血。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻触发 360% 范围伤害 + 击退
|
||||
6201: [{ lv: 1, t_num: 1, overrides: { ap: 150, bck: 30 } }],
|
||||
// 普攻吸血 35%
|
||||
6302: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Self, ap: 35 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:剑圣·剑刃风暴(360% 物伤 + 击退) + 吸血面罩(普攻 35% 吸血)。
|
||||
**适配调整**:`t_num=1` 高频触发,单场 30s 内吸血量需调平衡。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 壁垒贤者(Support,复刻"哀木涕·守护壁垒")
|
||||
|
||||
**核心机制**:开场全队护盾 + 驻场生命光环。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.FStart]: {
|
||||
// 开场全队护盾(数值减免型)
|
||||
6301: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } }],
|
||||
},
|
||||
[SkillTriggerType.Field]: [
|
||||
// 驻场生命光环
|
||||
{ lv: 1, s_uuid: 7007, value: 15 }, // +15% 最大生命
|
||||
],
|
||||
```
|
||||
|
||||
**复刻自**:哀木涕·守护壁垒(开场 10% 最大生命护盾 + 防御光环)。
|
||||
**适配调整**:本系统护盾是"数值减免"非"层数",ap 字段意义不同,需重新校准数值。
|
||||
|
||||
---
|
||||
|
||||
### 3.5 凤凰祭司(Warrior,复刻"死亡守护 + 复活")
|
||||
|
||||
**核心机制**:死亡时为最低血友军回血 + 自身可复活。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Dead]: {
|
||||
// 死亡守护:死亡时为最低血友军回血
|
||||
6302: [{ lv: 1, t_num: 1, overrides: { ap: 50 } }],
|
||||
},
|
||||
[SkillTriggerType.Revive]: [
|
||||
// 复活:死亡后 1 次 50% 血复活
|
||||
{ lv: 1, s_uuid: 6501, r_num: 1, upr: 50 },
|
||||
],
|
||||
```
|
||||
|
||||
**复刻自**:通用觉醒·死亡守护(10% 最大生命回血) + 黑暗骑士 3 觉(尸骸存活)。
|
||||
**适配调整**:30s 战斗中复活价值高,`r_num=1` 限定 1 次。
|
||||
|
||||
---
|
||||
|
||||
### 3.6 暗影骑士(Tank,复刻"黑暗骑士·死亡缠绕")
|
||||
|
||||
**核心机制**:普攻治疗血少友军 + 受击反击自盾。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻触发治疗(自动选血量最低友方,引擎已支持)
|
||||
6302: [{ lv: 1, t_num: 1, overrides: { ap: 220 } }],
|
||||
},
|
||||
[SkillTriggerType.Atked]: {
|
||||
// 受击触发自身护盾
|
||||
6301: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Self, ap: 3 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:黑暗骑士·死亡缠绕(普攻 25% 概率 220% 真伤/治疗友方)。
|
||||
**适配调整**:本系统暂无"真伤",用高治疗量替代。
|
||||
|
||||
---
|
||||
|
||||
### 3.7 风暴歌者(Mage,复刻"深渊女王·超声波")
|
||||
|
||||
**核心机制**:范围伤害 + 群体击退。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 范围伤害 + 群体击退
|
||||
6201: [{ lv: 1, t_num: 1, overrides: { ap: 200, bck: 50, hit_count: 6 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:深渊女王·超声波(伤害 + 击退控制)。
|
||||
**适配调整**:`bck` 字段直接对应击退距离增量,无需新增机制。
|
||||
|
||||
---
|
||||
|
||||
### 3.8 战吼先锋(Warrior,复刻"光明骑士·光明守护")
|
||||
|
||||
**核心机制**:开场全队增益 + 持续回血。
|
||||
|
||||
```typescript
|
||||
[SkillTriggerType.FStart]: {
|
||||
// 开场全队攻击强化
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { ap: 30 } }],
|
||||
// 开场全队持续回血
|
||||
6502: [{ lv: 1, t_num: 1, overrides: { timed_buff_id: 7010, buff_duration: 10 } }],
|
||||
},
|
||||
```
|
||||
|
||||
**复刻自**:光明骑士·光明守护(清异常 + 加防 + 持续治疗)。
|
||||
**适配调整**:本系统无"解控"概念,30s 战斗也不需要,改为纯增益 + HoT。
|
||||
|
||||
---
|
||||
|
||||
## 四、需要小幅扩展即可复刻
|
||||
|
||||
### 4.1 反伤罗刹(Tank,复刻"剑豪·罗生门")
|
||||
|
||||
**缺失机制**:本系统无"反伤"属性。
|
||||
|
||||
**扩展方案**:
|
||||
1. `HeroAttrs.ts` 的 `Attrs` 枚举新增 `thorns = "thorns"`(反伤数值)
|
||||
2. `HeroAttrsComp.ts` 新增字段 `thorns: number = 0`
|
||||
3. 战斗结算时,受击方对攻击者造成 `thorns` 点固定伤害
|
||||
4. `SkillConfig` 新增 `thorns?: number` 用于 Support 技能附加反伤 buff
|
||||
|
||||
**复刻配置**:
|
||||
```typescript
|
||||
[SkillTriggerType.Atked]: {
|
||||
// 受击触发护盾 + 反伤 buff(5s 内反伤 30 点)
|
||||
6301: [{ lv: 1, t_num: 1, overrides: { ap: 5 } }],
|
||||
6401: [{ lv: 1, t_num: 1, overrides: { buff_type: Attrs.thorns, timed_buff_id: 7030 } }],
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 真伤刺客(Assassin,复刻"玉面狐妖·真伤")
|
||||
|
||||
**缺失机制**:本系统伤害公式只支持 `ap × 系数`,无"按目标最大生命百分比伤害"。
|
||||
|
||||
**扩展方案**:
|
||||
1. `SkillConfig` 新增 `target_hp_pct?: number`(按目标最大生命百分比扣血)
|
||||
2. 伤害结算时,若技能含此字段,额外造成 `target.hp_max × target_hp_pct / 100` 伤害
|
||||
|
||||
**复刻配置**:
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻附带 3% 目标最大生命真伤
|
||||
6001: [{ lv: 1, t_num: 1, overrides: { ap: 100, /* 需新增 target_hp_pct: 3 */ } }],
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3 重伤诅咒(Mage,复刻"巫妖王·重伤")
|
||||
|
||||
**缺失机制**:本系统无"降低治疗效果"属性。
|
||||
|
||||
**扩展方案**:
|
||||
1. `Attrs` 新增 `heal_reduce = "heal_reduce"`(百分比降低受治疗)
|
||||
2. 治疗结算时,实际治疗量 = 原治疗量 × (1 - heal_reduce/100)
|
||||
|
||||
**复刻配置**:
|
||||
```typescript
|
||||
[SkillTriggerType.Atking]: {
|
||||
// 普攻附带重伤 debuff(敌方受治疗 -50%)
|
||||
6101: [{ lv: 1, t_num: 1, overrides: { /* 需新增 heal_reduce 机制 */ } }],
|
||||
},
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、不建议复刻
|
||||
|
||||
| 类型 | 代表技能 | 原因 |
|
||||
|---|---|---|
|
||||
| **能量/大招系统** | 所有英雄的"大招" | 本系统无能量条,技能全靠触发器,无法承载"释放大招"概念 |
|
||||
| **解控** | 光明洗礼、暗黑盾解控 | 30s 战斗节奏快,控制时间仅 2~3s,解控价值极低 |
|
||||
| **多段位移/分身** | 炼狱鬼斩、大圣虚影 | 视觉表现层复杂,ECS 弹道模型难承载"幻影穿梭" |
|
||||
| **沉默 / 减能** | 潮壮壮、海拉 | 本系统无主动技能,沉默无目标可作用 |
|
||||
| **长期叠层** | 剑圣 1 觉(30% 防御转攻击,永久) | 30s 战斗内永久叠层易失衡 |
|
||||
| **按当前生命%伤害** | 女王尖啸(18% 当前生命) | 与"按最大生命%"相比,30s 短局内波动过大,难平衡 |
|
||||
| **击退抗性 / 减速抗性** | 剑豪红武、酒仙 | 本系统已有 `knockback_res`,但减速概念不存在 |
|
||||
|
||||
---
|
||||
|
||||
## 六、复刻优先级矩阵
|
||||
|
||||
| 优先级 | 英雄 | 复刻难度 | 设计价值 |
|
||||
|---|---|---|---|
|
||||
| **P0(必做)** | 剧毒女王 | ✅ 零代码 | 引入 DoT 体系,丰富伤害维度 |
|
||||
| **P0** | 猴王·乱棍 | ✅ 零代码 | 多段伤害+击晕,近战核心 |
|
||||
| **P0** | 剑圣·疾风 | ✅ 零代码 | 范围+击退+吸血,全能战士 |
|
||||
| **P1(推荐)** | 壁垒贤者 | ✅ 零代码 | 团队防御核心 |
|
||||
| **P1** | 凤凰祭司 | ✅ 零代码 | 复活机制已有,强化死亡触发 |
|
||||
| **P1** | 暗影骑士 | ✅ 零代码 | 治疗型坦克,差异化定位 |
|
||||
| **P2(可选)** | 风暴歌者 | ✅ 零代码 | 范围控制,与剧毒女王互补 |
|
||||
| **P2** | 战吼先锋 | ✅ 零代码 | 开场增益,节奏型辅助 |
|
||||
| **P3(扩展后做)** | 反伤罗刹 | ⚙️ 需新增 thorns | 反伤 META,针对高攻速敌人 |
|
||||
| **P3** | 真伤刺客 | ⚙️ 需新增 target_hp_pct | 针对高血量 Boss |
|
||||
| **P3** | 重伤诅咒 | ⚙️ 需新增 heal_reduce | 针对治疗队 |
|
||||
|
||||
---
|
||||
|
||||
## 七、新增机制工作量评估
|
||||
|
||||
| 机制 | 改动文件 | 改动量 | 风险 |
|
||||
|---|---|---|---|
|
||||
| **反伤 thorns** | HeroAttrs.ts + HeroAttrsComp.ts + 战斗结算 | ~50 行 | 低(独立属性) |
|
||||
| **按最大生命%伤害** | SkillSet.ts + 伤害结算 | ~30 行 | 低(额外乘区) |
|
||||
| **减疗 heal_reduce** | HeroAttrs.ts + HeroAttrsComp.ts + 治疗结算 | ~40 行 | 低(独立乘区) |
|
||||
| **敌方 Support 技能** | SCastSystem.ts | ~20 行 | 中(需测试目标选择) |
|
||||
|
||||
---
|
||||
|
||||
## 八、与原"20 回合制"的兼容性
|
||||
|
||||
本设计文档基于 **1 回合定生死** 机制,所有数值均按 **30 秒战斗窗口** 校准:
|
||||
|
||||
- `t_num=1` 触发:30s 内普攻 30~50 次,技能触发 30~50 次
|
||||
- DoT/HoT 持续 5s:覆盖 1/6 战斗时长,效果显著但不至于"挂上就赢"
|
||||
- 复活 `r_num=1`:单场战斗最多 1 次,价值极高但可控
|
||||
- 驻场光环 `value=15%`:30s 内持续生效,等价于"永久"(单场)
|
||||
|
||||
若未来回归多回合制,需重新校准:
|
||||
- `t_num` 提高到 2~4(降低触发频率)
|
||||
- DoT/HoT 持续时间延长至 10~15s
|
||||
- 复活次数可放开到 2~3 次
|
||||
|
||||
---
|
||||
|
||||
## 九、下一步行动
|
||||
|
||||
1. **确认 P0 英雄清单**:是否按本方案复刻 8 个英雄?
|
||||
2. **数值校准**:是否需要先用 `calcHeroPower` 公式推导每个英雄的目标强度锚点?
|
||||
3. **新增机制决策**:是否在第 1 阶段引入反伤/真伤/减疗?还是先纯配置复刻?
|
||||
4. **编写 heroSet.ts 配置**:确认后我可以直接输出可粘贴的 TypeScript 配置代码。
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:v1.0
|
||||
**创建日期**:2026-08-14
|
||||
**作者**:AI Assistant
|
||||
**关联文档**:[heroSet.ts](./heroSet.ts) / [SkillSet.ts](./SkillSet.ts) / [BuffSet.ts](./BuffSet.ts) / [几何王国英雄技能汇总.md](./几何王国英雄技能汇总.md)
|
||||
11
assets/script/game/common/config/几何王国技能复刻方案.md.meta
Normal file
11
assets/script/game/common/config/几何王国技能复刻方案.md.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"importer": "text",
|
||||
"imported": true,
|
||||
"uuid": "4b91da8c-0510-4f36-a3f7-b78ec260c1f9",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
199
assets/script/game/common/config/几何王国英雄技能汇总.md
Normal file
199
assets/script/game/common/config/几何王国英雄技能汇总.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# 《几何王国》(微信小游戏)英雄技能汇总
|
||||
|
||||
> **资料说明**:本作暂无官方公开的完整技能数据库,以下内容整理自玩家攻略站、公众号「英雄解析」系列、社区讨论等公开资料(来源见文末)。技能数值可能随版本更新调整,请以游戏内实际为准。游戏现有 45+ 英雄,本文收录主流热门英雄。
|
||||
|
||||
---
|
||||
|
||||
## 一、技能体系说明
|
||||
|
||||
每个英雄的技能按养成线逐步解锁 [^7^][^27^]:
|
||||
|
||||
| 解锁节点 | 技能类型 |
|
||||
|---|---|
|
||||
| 初始 | 大招(主动技能) |
|
||||
| 2 星 | 战技技能 |
|
||||
| 5 星 | 强化技能 |
|
||||
| 8 星 | 特技技能 |
|
||||
| 12 星 | 属性技能 |
|
||||
| 16 星 | 强化技能 |
|
||||
| 觉醒 | 一觉 ~ 四觉(部分为通用技能池) |
|
||||
| 神器 | 紫 / 黄 / 红 / 彩 四档神器加成 |
|
||||
|
||||
---
|
||||
|
||||
## 二、热门英雄详细技能
|
||||
|
||||
### 1. 黑暗骑士(神话 · 前排主坦,版本必备)
|
||||
|
||||
- **大招「暗光之盾」**:释放无视控制;获得 8% 最大生命值的护盾并转化为生命(持续 7 秒),其中 5% 作用于队友;提供 20% 减速抗性 [^18^]
|
||||
- **缠绕(死亡缠绕)**:普攻有 25% 几率对全场血量最低的目标施放——敌方目标受到 220%(+10% 神器增强)真实伤害,友方目标回复等量生命 [^18^][^75^]
|
||||
- **暗黑盾**:解除控制,且附带击退效果 [^18^]
|
||||
- **神器「霜之暗斧」**:叠加 3 层可沉默敌方前排并减速 [^18^]
|
||||
- **彩色神器「邪恶光环」**:+8% 移动速度光环 + 少量回复 [^18^]
|
||||
- **觉醒**:1 觉全队 +10% 魔抗;3 觉阵亡后以尸体状态存活 3.5 秒,为后排争取输出时间 [^18^]
|
||||
- **定位评价**:目前版本最全面的前排,治疗、推进、解控兼具,基本是必备品 [^18^]
|
||||
|
||||
### 2. 光明骑士(神话 · 前排辅助)
|
||||
|
||||
- **大招「光明守护」**:释放无视控制,清除全队异常状态;全队 +30% 防御、+10% 能量回复速度;每秒 35% 攻击力的治疗,持续 7 秒 [^18^]
|
||||
- **圣洁之锤**:120% 真实伤害,伤害的 70% 转化为自身治疗 [^18^]
|
||||
- **光明洗礼**:每 6 秒触发一次,为 2 名队友解除异常状态(可解沉默、控制、减攻等)[^18^]
|
||||
- **彩色神器「圣域守护」**:减少敌方 20% 攻速、10% 移速;每 10 秒给队友单体释放光明守护 [^18^]
|
||||
|
||||
### 3. 剑圣(神话 · 前排输出)
|
||||
|
||||
- **大招「剑刃风暴」**:360% 物理伤害,击退所有角色,附带 6 秒 20% 重伤 [^68^]
|
||||
- **战技「致命一击」**:普攻 35% 几率造成 1.5~2.2 倍真实伤害 [^68^]
|
||||
- **分身斩**:每隔一段时间造成 5 次伤害 [^68^]
|
||||
- **觉醒**:1 觉将 30% 防御转换为攻击;3 觉普攻随机降低对方攻击 [^68^]
|
||||
- **评价**:免疫技能、群体击退,能聚集敌方后排利于己方 AOE;但伤害多为打前排的"无效输出",定位偏功能性抗伤 + 击退 [^68^]
|
||||
|
||||
### 4. 酒仙(神话 · 为推进而生)
|
||||
|
||||
- **风暴战士**:清除负面效果,击退抗性 +100%,伤害减免 30%(无法减免真伤),75% 免控 [^68^]
|
||||
- **雷霆一击**:普攻 30% 概率使范围内敌人全体减速并强力击退 [^68^]
|
||||
- **酒雾火焰**:每隔一段时间造成伤害并附加 5 秒 50% 重伤效果 [^68^]
|
||||
- **觉醒**:1 觉普攻随机降低目标 6% 攻击或 10% 防御;3 觉血量低于 50% 时,5 秒免疫魔法或物理伤害 [^68^]
|
||||
|
||||
### 5. 剑豪(前排战士 · 针对射手/物理队)
|
||||
|
||||
作用:输出 + 沉默 + 减攻 + 减退抗 + 反伤;BUFF/DEBUFF 全面,进可攻退可守,缺点是缺少硬控 [^7^]
|
||||
|
||||
- **大招「炼狱鬼斩」**:制造幻影在战场穿梭发动 6 次斩击,每次 360% 攻击的物理伤害并附带击退,每个目标至多承受 3 次,优先斩击物理伤害单位 [^7^]
|
||||
- **2 星战技「三十六烦恼凤」**:每 10 秒释放剑气攻击敌方能量最高单位,500% 物理伤害 + 沉默 1.5 秒(可抢第一波大招先手)[^7^]
|
||||
- **5 星强化「强化斩击」**:大招命中攻击高于自己的目标时,降低其 15% 攻击 6 秒 [^7^]
|
||||
- **8 星特技「罗生门」**:每累计损失 25% 最大生命,生成剑门屏障,抵消 18% 伤害 + 25% 反伤,持续 4 秒,无冷却(核心技能)[^7^]
|
||||
- **12 星「武装色霸气」**:攻击 +10%、生命 +15% [^7^]
|
||||
- **16 星「修罗之道」**:大招每次斩击额外造成 100% 真实伤害,并降目标 15% 击退抗性 5 秒 [^7^]
|
||||
- **神器**:紫武(战技沉默结束后降目标 30% 回能 5 秒);黄武(罗生门反伤增至 55%、持续增至 6 秒);**红武「三千世界」**(入场 13 秒后目标承受伤害 +27%,自身受沉默时间 -60%);彩武(罗生门释放时立即回复 11% 最大生命 + 回能/回血加成)[^7^]
|
||||
- **觉醒「空手接白刃」**:生命低于 55% 时,永久降低敌方全体 15% 物理伤害直至阵亡 [^7^]
|
||||
- **培养建议**:神器最低门槛红色,推荐彩色,优先级高 [^7^]
|
||||
|
||||
### 6. 至尊宝(前排战士 · 功能性工具人)
|
||||
|
||||
作用:击退 + 眩晕 + 减防 + 受伤回血 + 扛伤;能抗能解控,劣势局有反打之力,但输出低 [^27^]
|
||||
|
||||
- **大招「大圣归来」**:召唤大圣虚影,期间自身攻击 +25%;乱棍打击 4 次 ×30% 物理伤害并击退,收尾一击 160% 物理伤害 + 2 秒眩晕 [^27^]
|
||||
- **2 星战技「七伤拳」**:普攻 30% 概率降低目标 30% 防御,但 20% 概率降低自身 10% 防御 [^27^]
|
||||
- **5 星强化「大圣之力」**:放大招时自身 +15% 击退效果 3 秒 [^27^]
|
||||
- **8 星特技「般若波罗蜜」**:每 12 秒引导时空之力,3 秒内受到伤害的 60% 转化为自身生命(不受重伤影响,转化总量不超过 12% 最大生命)[^27^]
|
||||
- **12 星「五岳之尊」**:防御 +10%、生命 +15%;**16 星「大圣之御」**:放大招时受伤 -30% 持续 6 秒 [^27^]
|
||||
- **神器**:紫武(七伤拳触发概率 +10%);黄武(般若波罗蜜可作用于所有近战友军)[^27^]
|
||||
|
||||
### 7. 齐天大圣(神话 · T0 全能战士,人气主 C)
|
||||
|
||||
- **主动技能「定海神针」**:对敌方前排造成范围眩晕效果,打乱敌方阵型 [^35^]
|
||||
- 技能与普攻均可造成高额暴击伤害,后期伤害依然可观 [^35^][^33^]
|
||||
- 强度榜长期位居 T0,获取与培养成本低,新手友好 [^33^][^37^]
|
||||
|
||||
### 8. 复仇者(神话 · T0 后排杀手)
|
||||
|
||||
- **「暗影突袭」**:可穿透威胁敌方后排关键目标 [^2^]
|
||||
- 攻击敌人附加流血效果;获取成本较低,性价比极高 [^33^][^1^]
|
||||
- 阵容中可搭配火枪,用斩击 + 狙击双重伤害秒杀敌方后排 [^14^]
|
||||
|
||||
### 9. 深渊女王 / 深渊女巫(法师 · 群体输出 + 控制)
|
||||
|
||||
- **大招「超声波」**:主力伤害 + 击退控制技能 [^60^]
|
||||
- **2 星战技「剧毒利刃」**:普攻 30% 几率射出淬毒匕首,200% 魔法伤害,3 秒内降低目标 20% 击退抗性 [^28^]
|
||||
- **5 星强化「强化声波」**:超声波击退效果 +30% [^28^]
|
||||
- **8 星特技「女王尖啸」**:战斗第 20 秒发出尖啸,使所有敌方目标失去 18% 当前生命并眩晕 2 秒(每场仅 1 次,上限 350% 攻击)[^28^]
|
||||
- **12 星「攻击增强」**:攻击 +25%;**16 星「超级声波」**:超声波魔法伤害 +65%,被击中目标 5 秒内伤害结果 -25% [^28^]
|
||||
|
||||
### 10. 巫妖王(法师 · 当前版本核心)
|
||||
|
||||
- **核心能力是红武提供的"重伤"**——这种能力全英雄中稀缺,在爷爷、光明骑士遍地走的环境下接近必需品 [^3^]
|
||||
- 彩神具有**多段控制**(连环霜冻,黑暗骑士与光明骑士都解控不过来)[^3^]
|
||||
- 自带**减速和冰冻能力**,利于推进甚至壁咚,集控制、输出、重伤、推力于一体 [^3^]
|
||||
- 常见搭配:巫妖王 + 黑暗骑士 + 光明骑士 + 战士(复仇者/哪吒/剑圣/船长)+ 暗器大师或影魔 [^3^]
|
||||
|
||||
### 11. 玉面狐妖(法师 · 真伤输出)
|
||||
|
||||
- 前排杀手队核心之一:真实伤害无视防御,配合爷爷的攻击与回能增益可快速消灭敌方前排 [^14^]
|
||||
- 后期值得培养的法师主 C 之一 [^1^]
|
||||
|
||||
---
|
||||
|
||||
## 三、英雄觉醒技能表
|
||||
|
||||
以下为各英雄觉醒技能(一觉~四觉,含通用技能池)[^4^]:
|
||||
|
||||
| 英雄 | 觉醒技能 |
|
||||
|---|---|
|
||||
| **斧小王** | 坚硬皮肤(受伤 +1.5% 防御,最多 12 层);梦魇肩甲;死亡守护;嗜血战斧(每次释放「淘汰之斧」回复 15% 已损生命) |
|
||||
| **剑圣** | 吸血面罩(普攻伤害的 35% 转化为自身生命);攻防转换(30% 防御转攻击,50% 几率降敌 5% 攻击 5 秒);梦魇肩甲;死亡守护 |
|
||||
| **剑豪** | 空手接白刃(生命低于 55% 时永久降敌方全体 15% 物理伤害);梦魇肩甲;死亡守护;见闻色霸气(己方半场 +22% 击退抗性 / 敌方半场 +22% 控制抗性) |
|
||||
| **酒仙** | 重拳(普攻随机降 6% 攻击或降 10% 防御 5 秒);元素抗性(血量首次低于 50%,5 秒免疫物理或魔法伤害) |
|
||||
| **哪吒** | 三味真火(攻击附加 3% 最大生命伤害,上限 150% 攻击);莲花护体(每损失 10% 生命 +10% 受伤回能) |
|
||||
| **八神** | 狂暴之怒(15% 伤害转化为自身生命);疯狂之血(血量低于 50%,+5% 攻击 +5% 破甲) |
|
||||
| **哀木涕** | 守护壁垒(开场获得 10% 最大生命护盾,自身与身后友军防御 +6%);战斗意志(每累计损失 20% 生命 +10% 受伤回能 +5% 格挡,最多 6 层) |
|
||||
|
||||
**通用觉醒技能**:
|
||||
|
||||
- **梦魇肩甲**:受到攻击时,使攻击者 6 秒内对自己造成的伤害降低 2%(最多 3 层)[^4^]
|
||||
- **死亡守护**:自身死亡时,为血量最低的友军回复 10% 最大生命值 [^4^]
|
||||
|
||||
---
|
||||
|
||||
## 四、其他英雄技能速览
|
||||
|
||||
| 英雄 | 定位 | 技能特点 |
|
||||
|---|---|---|
|
||||
| 骷髅王 | 前排/输出 | 大招对敌方全体施加诅咒 + 暴击伤害,通用性强 [^33^] |
|
||||
| 月之女 | 输出 | 爆发能力极强,技能持续叠加伤害,每次释放输出更高 [^33^][^47^] |
|
||||
| 潮壮壮 | 前排/控制 | 技能命中造成沉默,让敌人无法放技能;攻防兼备,泛用性强,PVP 必备且不太吃练度 [^33^][^47^] |
|
||||
| 火枪 | 射手 | 「狙击」致命打击敌方后排,与复仇者斩击配合可双重秒杀后排 [^14^] |
|
||||
| 船长 | 战士 | 幽灵船、舰船突袭:高额伤害 + 大幅击退,暴力推进队核心 [^14^] |
|
||||
| 术士 | 辅助 | 地狱火有强大推进能力并可作前排分摊伤害;技能带减能、减疗、减速 [^14^] |
|
||||
| 海拉 | 法师 | 技能带减能、减疗、沉默,控制折磨队核心 [^14^] |
|
||||
| 沉默 | 辅助 | 技能带减能、沉默效果 [^14^] |
|
||||
| 疾风忍者 | 前排 | 技能带减能、沉默效果 [^14^] |
|
||||
| 铁扇公主 | 法师 | 推进 + 以自身为中心约 2 个人物身位的光环 [^68^] |
|
||||
| 神罗战士 / 李白 | 战士 | 带真实伤害,无视防御,前排杀手 [^14^] |
|
||||
| 人鱼公主 | 辅助 | 提供控制,并为全队增加攻击力加成,提升队伍输出 [^1^][^39^] |
|
||||
| 莫莫 | 辅助 | 为队友提供减防支持,提升队伍生存与输出 [^1^] |
|
||||
| 月光使者 | 辅助 | 黄武可用,开荒期过渡英雄 [^3^] |
|
||||
| 爷爷(葫芦爷爷) | 辅助 | 为队友回能 + 加攻击,强化核心输出伤害 [^14^] |
|
||||
| 暗器大师 / 影魔 | 射手 | 巫妖王体系最推荐的射手位 [^3^] |
|
||||
|
||||
---
|
||||
|
||||
## 五、英雄强度榜(参考)
|
||||
|
||||
综合多个攻略站的强度评价 [^33^][^37^][^34^]:
|
||||
|
||||
- **T0**:复仇者、齐天大圣(另有榜单列入影刃、月之女)
|
||||
- **T1**:骷髅王、月之女、绿巨人
|
||||
- **T1.5**:剑圣
|
||||
- **T2**:潮壮壮、暗器大师、矮人飞机、双头龙
|
||||
- **T3**:光明骑士、术士(注:此榜为旧版评价,当前版本光明骑士、术士在特色阵容中仍有重要地位 [^14^])
|
||||
|
||||
**后期值得培养的主 C 推荐** [^1^]:
|
||||
|
||||
- 战士:船长、黑暗骑士、复仇者、剑圣、神罗战士、白牛
|
||||
- 辅助:冰女、小鹿、光明骑士、琴音仙女、术士、月光使者、沉默
|
||||
- 射手:火枪、影魔、骷髅射手、暗器大师
|
||||
- 法师:巫妖王、玉面狐妖、海拉、铁扇公主、深渊女王
|
||||
|
||||
---
|
||||
|
||||
## 参考来源
|
||||
|
||||
[^1^]: [几何王国游戏攻略/钓鱼/鱼缸搭配/英雄推荐 — 灯塔码铺](https://blog.share888.top/note/game/jihewangguo/index.html)
|
||||
[^2^]: [《几何王国》——快速养成,满满成就!— 小米游戏中心](https://game.xiaomi.com/viewpoint/1585024444_1751347565577_100)
|
||||
[^3^]: [《几何王国》该如何确定"终极"阵容和一些杂谈 — 雷电模拟器](https://www.ldmnq.com/0/zixun/1028749.html)
|
||||
[^4^]: [几何王国英雄觉醒技能表 — 7724游戏](http://www.7724.com/jhwg/news/188085.html)
|
||||
[^7^]: [【几何王国】英雄解析--剑豪 — 微信公众号](http://mp.weixin.qq.com/s?__biz=MzkxMjU1NTA2NA==&mid=2247485616&idx=1&sn=50f23d36776ee8d25f5916dee600caf7)
|
||||
[^14^]: [《几何王国》新英雄与新玩法背景下,如何搭配适应版本的多样阵容 — 雷电模拟器](https://m.ldmnq.com/0/zixun/1028757.html)
|
||||
[^18^]: [觉醒后英雄强度分析-前排篇(I)— 4399](https://a.4399.cn/gl/50173882_361641.html)
|
||||
[^27^]: [【几何王国】英雄解析--至尊宝 — 微信公众号](http://mp.weixin.qq.com/s?__biz=MzkxMjU1NTA2NA==&mid=2247485630&idx=1&sn=5df2e8c053ab873e78ecfae5db7dfcb3)
|
||||
[^28^]: [《几何王国》英雄攻略深渊女巫 — 百度贴吧](https://tieba.baidu.com/p/9105304613)
|
||||
[^33^]: [几何王国角色强度排行 — 3DM手游](https://shouyou.3dmgame.com/gl/517771.html)
|
||||
[^34^]: [几何王国角色排行表大全2025英雄强度排名 — 7724游戏](http://m.7724.com/jhwg/news/183870.html)
|
||||
[^35^]: [几何王国社区 — 小米游戏中心](https://game.xiaomi.com/gamePost/62405456)
|
||||
[^37^]: [几何王国角色强度排行 — 游侠手游](https://m.ali213.net/news/gl2404/1370787.html)
|
||||
[^39^]: [《几何王国》新手进阶全攻略 — 百度文库](https://word.baidu.com/view/e9324c26b8d126fff705cc1755270722192e5990.html)
|
||||
[^47^]: [几何王国最强英雄推荐 t0英雄强度排行 — 16手游网](http://www.16shouyou.com/gl/34278.html)
|
||||
[^60^]: [深渊女王的三个阶段分别是什么?— 百度贴吧·几何王国吧](https://tieba.baidu.com/p/9646144609)
|
||||
[^68^]: [《几何王国》觉醒后英雄强度分析-前排篇(I)— 雷电模拟器](https://m.ldmnq.com/0/zixun/1028753.html)
|
||||
[^75^]: [几何王国绝顶英雄主推2024 — 悟极手游网](https://www.wjmplastics.com/article/223.html)
|
||||
11
assets/script/game/common/config/几何王国英雄技能汇总.md.meta
Normal file
11
assets/script/game/common/config/几何王国英雄技能汇总.md.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"importer": "text",
|
||||
"imported": true,
|
||||
"uuid": "3ec17428-df2e-40bc-a707-4de99ee79b10",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import { smc } from "../common/SingletonModuleComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
|
||||
import { HeroInfo, HeroPos, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, calcGrowBonus } from "../common/config/heroSet";
|
||||
import { HeroInfo, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, calcGrowBonus, HeroDisVal } from "../common/config/heroSet";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { SkillTriggerType } from "../common/config/heroSet";
|
||||
import { SkillTriggerType, HType } from "../common/config/heroSet";
|
||||
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||
import { Attrs } from "../common/config/HeroAttrs";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
@@ -122,7 +122,8 @@ export class Hero extends ecs.Entity {
|
||||
model.grow_type = hero.grow_type;
|
||||
model.role = hero.role;
|
||||
model.fac = FacSet.HERO;
|
||||
model.dis = hero.dis ?? 720;
|
||||
// 攻击距离:配置优先;未配置按攻击定位取默认值(近战100 / 中程300 / 远程450)
|
||||
model.dis = hero.dis ?? HeroDisVal[hero.type as HType.Melee | HType.Mid | HType.Long];
|
||||
model.posIndex = posIndex;
|
||||
if (posIndex >= 0) {
|
||||
smc.mission.heroGrid[posIndex] = this.eid;
|
||||
@@ -132,7 +133,6 @@ export class Hero extends ecs.Entity {
|
||||
model.call = hero.call;
|
||||
model.dead = hero.dead;
|
||||
model.fstart = hero.fstart;
|
||||
model.fend = hero.fend;
|
||||
model.atking = hero.atking;
|
||||
model.atked = hero.atked;
|
||||
model.field = hero.field;
|
||||
@@ -171,7 +171,6 @@ export class Hero extends ecs.Entity {
|
||||
model.runtime_call = resolveTriggerByLv(model.call, hero_lv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, hero_lv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, hero_lv);
|
||||
model.runtime_fend = resolveTriggerByLv(model.fend, hero_lv);
|
||||
model.runtime_atking = resolveTriggerByLv(model.atking, hero_lv);
|
||||
model.runtime_atked = resolveTriggerByLv(model.atked, hero_lv);
|
||||
model.runtime_field = resolveFieldByLv(model.field, hero_lv);
|
||||
|
||||
@@ -17,6 +17,7 @@ import { mLogger } from "../common/Logger";
|
||||
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||
import { getMonsterGoldDrop } from "../map/RogueConfig";
|
||||
import { MissionEconomy } from "../map/MissionEconomy";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
|
||||
/** 最终伤害数据接口
|
||||
* 用于封装一次攻击计算的所有结果数据
|
||||
@@ -146,15 +147,15 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
}
|
||||
}
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after crit", damage)
|
||||
// 护盾吸收
|
||||
// 护盾数值减免(1 点护盾吸收 1 点伤害,扣完为止,溢出部分继续扣血)
|
||||
const shieldResult = this.absorbShield(TAttrsComp, damage);
|
||||
damage = shieldResult.remainingDamage;
|
||||
if (shieldResult.absorbedDamage > 0) {
|
||||
// 【评分系统 - 防御分】统计护盾成功抵挡伤害的次数
|
||||
smc.vmdata.scores.shield_block_count += 1;
|
||||
// 【评分系统 - 防御分】统计护盾累计吸收的伤害量
|
||||
smc.vmdata.scores.shield_block_count += Math.floor(shieldResult.absorbedDamage);
|
||||
}
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " after shield", damage)
|
||||
// 显示护盾格挡飘字
|
||||
// 显示护盾减免飘字
|
||||
if (shieldResult.absorbedDamage > 0 && targetView) {
|
||||
targetView.shield_tip(shieldResult.absorbedDamage);
|
||||
}
|
||||
@@ -175,11 +176,6 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
TAttrsComp.atked_count++;
|
||||
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
||||
|
||||
// 受击者产生击退效果
|
||||
// if (damage > 0 && targetView) {
|
||||
// targetView.back();
|
||||
// }
|
||||
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 英雄${TAttrsComp.hero_name} (uuid: ${TAttrsComp.hero_uuid}) 受到 eid:${casterEid} 的 伤害 ${damage},${isCrit ? "暴击" : "普通"}攻击,技能ID ${damageEvent.s_uuid}`);
|
||||
|
||||
// 冰冻判定
|
||||
@@ -190,14 +186,26 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
const stunChance = (damageEvent.Attrs[Attrs.stun_chance] || 0) - TAttrsComp.getFinalStunRes();
|
||||
const isStun = !TAttrsComp.isStun() && this.checkChance(stunChance);
|
||||
|
||||
// 击退判定
|
||||
const knockbackChance = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.knockback_res || 0);
|
||||
const isKnockback = this.checkChance(knockbackChance);
|
||||
// 击退:受击默认后退,击退属性不再作为概率,而是转换为后退距离的增量
|
||||
// 击退增量 = 施法者击退强化(原击退概率)- 被攻击者击退抗性 + 击退距离强化
|
||||
const knockbackBonus = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.getFinalKnockbackRes() || 0);
|
||||
const knockbackExtra = Math.max(0, knockbackBonus + (damageEvent.Attrs[Attrs.knockback_distance] || 0));
|
||||
|
||||
// ✅ 触发视图层表现(伤害数字、受击动画、冰冻、击晕、击退)
|
||||
if (targetView) {
|
||||
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, false);
|
||||
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, knockbackExtra);
|
||||
targetView.playEnd(skillConf.endAnm);
|
||||
|
||||
// 击退位移下沉到 MoveSystem:直接写入 knockbackVelocity,由 MoveSystem 每帧衰减并叠加位移
|
||||
const moveComp = target.get(MoveComp);
|
||||
if (moveComp) {
|
||||
const dist = FightSet.BACK_RANG + knockbackExtra;
|
||||
const isHero = TAttrsComp.fac === FacSet.HERO;
|
||||
// 击退初速度:0.1 秒时间常数衰减,总位移约等于 dist
|
||||
const knockbackSpeed = dist * 16;
|
||||
moveComp.knockbackVelocity = isHero ? -knockbackSpeed : knockbackSpeed;
|
||||
}
|
||||
|
||||
if (isFrost) {
|
||||
TAttrsComp.toFrost();
|
||||
targetView.in_iced(FightSet.FROST_TIME);
|
||||
@@ -212,18 +220,16 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
smc.vmdata.scores.stun_count++;
|
||||
}
|
||||
}
|
||||
if (isKnockback) {
|
||||
targetView.back(damageEvent.Attrs[Attrs.knockback_distance] || 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查死亡
|
||||
if (TAttrsComp.hp <= 0) {
|
||||
// 先触发死亡技能(如亡语),不管后续是否复活都应触发
|
||||
// 先触发死亡技能(如亡语),不管后续是否续命都应触发
|
||||
this.triggerDeadSkills(target);
|
||||
|
||||
// 复活机制:如果玩家属性内的复活属性值>=1 则执行复活,原地50%血量复活
|
||||
// 濒死回血(revive 系):不是真正复活,而是血量归零瞬间消耗 1 次续命次数,
|
||||
// 按比例回血继续战斗。次数每回合重置(见 MissionComp.healAllHeroes)。
|
||||
let canRevive = false;
|
||||
let maxReviveCount = 0;
|
||||
let reviveHpPercent = 50; // 默认恢复50%
|
||||
@@ -338,16 +344,16 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
|
||||
/**
|
||||
* 处理角色死亡
|
||||
*
|
||||
*
|
||||
* 死亡处理流程:
|
||||
* 1. 标记死亡状态(is_dead = true)
|
||||
* 2. 触发死亡事件(onDeath)
|
||||
* 3. 记录调试信息(如启用调试模式)
|
||||
*
|
||||
* 2. 英雄阵营:快照死亡状态到 smc.mission.dead_heroes(供后续复活机制重新召唤)
|
||||
* 3. 触发死亡事件(onDeath)
|
||||
*
|
||||
* @param entity 死亡的实体
|
||||
*
|
||||
*
|
||||
* @important 死亡状态一旦设置,该实体将不再处理新的伤害事件
|
||||
* 这确保了死亡逻辑的单一性和一致性
|
||||
* 英雄为真实死亡:视图动画结束后由 HeroViewComp.realDead 销毁实体
|
||||
*/
|
||||
private doDead(entity: ecs.Entity): void {
|
||||
const TAttrsComp = entity.get(HeroAttrsComp);
|
||||
@@ -355,6 +361,11 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
|
||||
TAttrsComp.is_dead = true;
|
||||
|
||||
// 英雄真实死亡:记录快照,供后续复活机制(重新召唤)消费
|
||||
if (TAttrsComp.fac === FacSet.HERO) {
|
||||
this.recordDeadHero(TAttrsComp);
|
||||
}
|
||||
|
||||
// 触发死亡事件
|
||||
this.onDeath(entity);
|
||||
|
||||
@@ -363,6 +374,24 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 快照死亡英雄的静态/成长数据到 smc.mission.dead_heroes。
|
||||
* 仅记录重新召唤所需字段,不含战斗内临时状态(buff、技能 CD 等)。
|
||||
*
|
||||
* @param attrs 死亡英雄的属性组件
|
||||
*/
|
||||
private recordDeadHero(attrs: HeroAttrsComp): void {
|
||||
smc.mission.dead_heroes.push({
|
||||
uuid: attrs.hero_uuid,
|
||||
lv: attrs.lv,
|
||||
card_lv: attrs.card_lv,
|
||||
posIndex: attrs.posIndex,
|
||||
wave: Math.max(1, smc.vmdata?.mission_data?.level ?? 1),
|
||||
});
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem',
|
||||
` 死亡英雄已记录: ${attrs.hero_name}(uuid=${attrs.hero_uuid}) lv=${attrs.lv} pos=${attrs.posIndex} wave=${smc.vmdata?.mission_data?.level}, 当前共 ${smc.mission.dead_heroes.length} 个`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -371,7 +400,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
* 用于所有概率相关的判定:
|
||||
* - 暴击率判定
|
||||
* - 闪避率判定
|
||||
* - 击退概率判定
|
||||
* - 冰冻/击晕概率判定
|
||||
* - 其他特殊效果概率
|
||||
*
|
||||
* @param rate 概率值(0-100的百分比)
|
||||
@@ -396,29 +425,26 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
|
||||
|
||||
/**
|
||||
* 护盾吸收伤害
|
||||
*
|
||||
* 护盾吸收逻辑:
|
||||
* 1. 护盾值大于 0 时,抵挡本次伤害(免伤)
|
||||
* 2. 每次抵挡消耗 1 点护盾值
|
||||
* 3. 护盾归零时,重置护盾最大值属性
|
||||
*
|
||||
* 护盾数值减免伤害
|
||||
*
|
||||
* 护盾为数值减免型:
|
||||
* 1. 1 点护盾吸收 1 点伤害
|
||||
* 2. 护盾扣减实际吸收量,溢出部分继续结算到血量
|
||||
*
|
||||
* @param TAttrsComp 被攻击者的属性组件(包含当前护盾值)
|
||||
* @param damage 原始伤害值
|
||||
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和被格挡的伤害值
|
||||
* @returns {remainingDamage, absorbedDamage} 剩余伤害值和被护盾减免的伤害值
|
||||
*/
|
||||
private absorbShield(TAttrsComp: HeroAttrsComp, damage: number): { remainingDamage: number, absorbedDamage: number } {
|
||||
if (TAttrsComp.shield <= 0) {
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " 护盾值小于等于0,无法吸收伤害");
|
||||
return { remainingDamage: damage, absorbedDamage: 0 };
|
||||
}
|
||||
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - 1);
|
||||
if (TAttrsComp.shield <= 0) {
|
||||
TAttrsComp.shield = 0;
|
||||
}
|
||||
const absorbed = Math.min(TAttrsComp.shield, damage);
|
||||
TAttrsComp.shield = Math.max(0, TAttrsComp.shield - absorbed);
|
||||
TAttrsComp.dirty_shield = true;
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾抵挡1次伤害,剩余次数 ${TAttrsComp.shield}`);
|
||||
return { remainingDamage: 0, absorbedDamage: damage };
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 护盾减免 ${absorbed.toFixed(1)} 点伤害,剩余护盾 ${TAttrsComp.shield.toFixed(1)}`);
|
||||
return { remainingDamage: damage - absorbed, absorbedDamage: absorbed };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,7 +47,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
[SkillTriggerType.Call]?: TriggerGrouped;
|
||||
[SkillTriggerType.Dead]?: TriggerGrouped;
|
||||
[SkillTriggerType.FStart]?: TriggerGrouped;
|
||||
[SkillTriggerType.FEnd]?: TriggerGrouped;
|
||||
[SkillTriggerType.Atking]?: TriggerGrouped;
|
||||
[SkillTriggerType.Atked]?: TriggerGrouped;
|
||||
/** 驻场光环(按 lv 解锁,存活期间全局生效) */
|
||||
@@ -68,8 +67,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
/** 当前等级下生效的战前触发条目 */
|
||||
runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
/** 当前等级下生效的战后触发条目 */
|
||||
runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[];
|
||||
/** 当前等级下生效的驻场光环词条列表 */
|
||||
runtime_field?: FieldEntry[];
|
||||
/** 当前等级下生效的复活配置 */
|
||||
@@ -82,9 +79,9 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
freeze_res: number = 0; // 冰冻抗性
|
||||
stun_chance: number = 0; // 击晕概率
|
||||
stun_res: number = 0; // 击晕抗性
|
||||
knockback_chance: number = 0; // 击退概率
|
||||
knockback_chance: number = 0; // 击退强化(原击退概率,现转换为击退距离增量)
|
||||
knockback_distance: number = 0; // 击退距离强化
|
||||
knockback_res: number = 0; // 击退抗性
|
||||
knockback_res: number = 0; // 击退抗性(削减击退距离增量)
|
||||
crit_damage: number = 0; // 额外暴击伤害
|
||||
puncture_chance: number = 0; // 穿透概率
|
||||
puncture_dmg_bonus: number = 0; // 穿透后伤害增量(百分点,乘算加成)
|
||||
@@ -153,11 +150,11 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
const addValue = Math.max(0, Math.floor(value));
|
||||
if (addValue <= 0) return;
|
||||
this.shield += addValue;
|
||||
this.shield = Math.min(this.shield, FightSet.SHIELD_MAX); // 限制护盾最大层数
|
||||
this.shield = Math.min(this.shield, FightSet.SHIELD_MAX); // 限制护盾值上限(数值减免型)
|
||||
if (this.shield < 0) this.shield = 0;
|
||||
this.dirty_shield = true; // 标记护盾需要更新
|
||||
if (this.debugMode) {
|
||||
mLogger.log(this.debugMode, 'HeroAttrs', ` 护盾次数变更: ${this.hero_name}, 变化=${addValue}, ${Math.floor(oldShield)} -> ${Math.floor(this.shield)}`);
|
||||
mLogger.log(this.debugMode, 'HeroAttrs', ` 护盾值变更: ${this.hero_name}, 变化=${addValue}, ${Math.floor(oldShield)} -> ${Math.floor(this.shield)}`);
|
||||
}
|
||||
}
|
||||
add_hp_max(value: number) {
|
||||
@@ -449,7 +446,7 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
return base + mods.flatSum + mods.pctSum;
|
||||
}
|
||||
|
||||
/** 最终击退概率 = 基础击退 + 限时修饰(加法模型) */
|
||||
/** 最终击退强化 = 基础击退强化 + 限时修饰(加法模型,转换为击退距离增量) */
|
||||
public getFinalKnockbackChance(): number {
|
||||
const mods = this.aggregateTimedMods(Attrs.knockback_chance);
|
||||
return this.knockback_chance + mods.flatSum + mods.pctSum;
|
||||
@@ -580,7 +577,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.call = undefined;
|
||||
this.dead = undefined;
|
||||
this.fstart = undefined;
|
||||
this.fend = undefined;
|
||||
this.atking = undefined;
|
||||
this.atked = undefined;
|
||||
this.field = undefined;
|
||||
@@ -592,7 +588,6 @@ export class HeroAttrsComp extends ecs.Comp {
|
||||
this.runtime_call = undefined;
|
||||
this.runtime_dead = undefined;
|
||||
this.runtime_fstart = undefined;
|
||||
this.runtime_fend = undefined;
|
||||
this.runtime_field = undefined;
|
||||
this.runtime_revive = undefined;
|
||||
this.critical = 0;
|
||||
|
||||
173
assets/script/game/hero/HeroTopBarComp.ts
Normal file
173
assets/script/game/hero/HeroTopBarComp.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { _decorator, ProgressBar, Sprite, UIOpacity, Vec3, v3, tween, Tween, Color, clamp } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/**
|
||||
* 英雄头顶状态栏组件
|
||||
*
|
||||
* 挂载在英雄节点下的 "top" 子节点上,独立管理:
|
||||
* - 血条进度(ProgressBar)
|
||||
* - 护盾数值条(ProgressBar,top/shield 节点,显示 shield / SHIELD_MAX)
|
||||
* - 透明度激活 / 闲置
|
||||
* - 受击抖动动画
|
||||
*
|
||||
* 数据流:HeroViewComp 将 HeroAttrsComp 引用传入,本组件通过 model 读取属性。
|
||||
* 注意:英雄根节点下的 "shielded" 是护盾特效动画节点,由 HeroViewComp 管理,不在本组件职责内。
|
||||
*/
|
||||
@ccclass('HeroTopBarComp')
|
||||
@ecs.register('HeroTopBar', false)
|
||||
export class HeroTopBarComp extends CCComp {
|
||||
|
||||
// ==================== @property 绑定(编辑器拖拽) ====================
|
||||
@property({ type: ProgressBar, tooltip: "血条进度条" })
|
||||
private hpProgressBar: ProgressBar = null!;
|
||||
|
||||
@property({ type: ProgressBar, tooltip: "护盾数值条(top 下的 shield 节点)" })
|
||||
private shieldProgressBar: ProgressBar = null!;
|
||||
|
||||
@property({ type: Sprite, tooltip: "血条填充 Sprite(用于设置阵营颜色)" })
|
||||
private hpBarSprite: Sprite = null!;
|
||||
|
||||
// ==================== 内部引用 ====================
|
||||
private topOpacity: UIOpacity = null!;
|
||||
private basePos: Vec3 = v3();
|
||||
|
||||
// ==================== 配置 ====================
|
||||
private hp_height: number = 90;
|
||||
private boss_hp_height: number = 160;
|
||||
|
||||
// ==================== 透明度 ====================
|
||||
private readonly barIdleOpacity: number = 153;
|
||||
private readonly barActiveOpacity: number = 255;
|
||||
private readonly idleOpacityDelay: number = 0.25;
|
||||
|
||||
/** 外部赋值:英雄数据模型 */
|
||||
private _model: HeroAttrsComp = null!;
|
||||
public get model(): HeroAttrsComp { return this._model; }
|
||||
public set model(value: HeroAttrsComp) { this._model = value; }
|
||||
|
||||
private readonly restoreBarIdleOpacity = () => {
|
||||
this.setTopBarOpacity(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* 初始化 / 对象池复用时重置
|
||||
* @param model 英雄属性组件
|
||||
* @param direction 显示方向(1 或 -1)
|
||||
*/
|
||||
public init(model: HeroAttrsComp, direction: number = 1): void {
|
||||
this._model = model;
|
||||
|
||||
// UIOpacity 无 @property 绑定,运行时获取或添加
|
||||
this.topOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
|
||||
|
||||
// 方向取 abs,避免对象池复用时 scale 累乘导致血条反转
|
||||
this.node.setScale(direction * Math.abs(this.node.scale.x), 1 * this.node.scale.y);
|
||||
|
||||
// 位置
|
||||
const height = model.is_boss ? this.boss_hp_height : this.hp_height;
|
||||
this.node.setPosition(0, height, 0);
|
||||
this.basePos = this.node.position.clone();
|
||||
|
||||
// 血条颜色(英雄阵营为绿色)
|
||||
if (model.fac === FacSet.HERO && this.hpBarSprite) {
|
||||
this.hpBarSprite.color = new Color("#2ECC71");
|
||||
}
|
||||
|
||||
// 确保血条等 UI 始终在最上层
|
||||
this.node.setSiblingIndex(999);
|
||||
|
||||
// 重置显示状态
|
||||
this.node.getChildByName("hp")!.active = true;
|
||||
this.node.getChildByName("cd")!.active = false;
|
||||
this.node.getChildByName("lv")!.active = false;
|
||||
this.node.active = true;
|
||||
|
||||
this.setTopBarOpacity(false);
|
||||
this.hpShow();
|
||||
this.shieldShow(model.shield);
|
||||
}
|
||||
|
||||
/** 更新血量进度条 */
|
||||
public hpShow(): void {
|
||||
if (!this._model || !this.hpProgressBar) return;
|
||||
const hp = this._model.hp;
|
||||
const hpMax = this._model.hp_max;
|
||||
let targetProgress = hpMax > 0 ? hp / hpMax : 0;
|
||||
targetProgress = clamp(targetProgress, 0, 1);
|
||||
|
||||
const prevProgress = this.hpProgressBar.progress;
|
||||
if (targetProgress < prevProgress) {
|
||||
this.activateTopBar();
|
||||
this.playHpBarShake();
|
||||
}
|
||||
this.hpProgressBar.progress = targetProgress;
|
||||
if (targetProgress > prevProgress) {
|
||||
this.setTopBarOpacity(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** 更新护盾数值条(百分比 = shield / SHIELD_MAX) */
|
||||
public shieldShow(shield: number = 0): void {
|
||||
if (!this.shieldProgressBar) return;
|
||||
const progress = FightSet.SHIELD_MAX > 0 ? shield / FightSet.SHIELD_MAX : 0;
|
||||
this.shieldProgressBar.progress = clamp(progress, 0, 1);
|
||||
// 护盾节点显隐
|
||||
this.shieldProgressBar.node.active = shield > 0;
|
||||
}
|
||||
|
||||
/** 激活顶栏(高亮 + 延迟恢复闲置透明度) */
|
||||
public activateTopBar(): void {
|
||||
this.setTopBarOpacity(true);
|
||||
this.unschedule(this.restoreBarIdleOpacity);
|
||||
this.scheduleOnce(this.restoreBarIdleOpacity, this.idleOpacityDelay);
|
||||
}
|
||||
|
||||
/** 设置顶栏透明度 */
|
||||
public setTopBarOpacity(isActive: boolean): void {
|
||||
if (!this.node || !this.node.isValid) return;
|
||||
if (this.topOpacity) {
|
||||
this.topOpacity.opacity = isActive ? this.barActiveOpacity : this.barIdleOpacity;
|
||||
}
|
||||
this.node.active = true;
|
||||
}
|
||||
|
||||
/** 显示顶栏 */
|
||||
public show(): void {
|
||||
if (this.node && this.node.isValid) this.node.active = true;
|
||||
}
|
||||
|
||||
/** 隐藏顶栏 */
|
||||
public hide(): void {
|
||||
if (this.node && this.node.isValid) this.node.active = false;
|
||||
}
|
||||
|
||||
/** 血条受击抖动动画 */
|
||||
private playHpBarShake(): void {
|
||||
if (!this.node || !this.node.isValid) return;
|
||||
Tween.stopAllByTarget(this.node);
|
||||
this.node.setPosition(this.basePos);
|
||||
tween(this.node)
|
||||
.by(0.04, { position: v3(-3, 0, 0) })
|
||||
.by(0.04, { position: v3(6, 0, 0) })
|
||||
.by(0.04, { position: v3(-5, 0, 0) })
|
||||
.by(0.04, { position: v3(2, 0, 0) })
|
||||
.call(() => {
|
||||
this.node.setPosition(this.basePos);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
/** 对象池回收 / 组件重置时调用 */
|
||||
public reset(): void {
|
||||
this.unscheduleAllCallbacks();
|
||||
if (this.node && this.node.isValid) {
|
||||
Tween.stopAllByTarget(this.node);
|
||||
this.node.setPosition(this.basePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a3113d69-645e-4a4b-bf68-a434fcbd6d8d",
|
||||
"uuid": "c4181fc5-1168-4a6d-8d03-d0f2dd0b22a2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Vec3, _decorator, v3, Collider2D, Contact2DType, Label, Node, Prefab, instantiate, ProgressBar, Component, Material, Sprite, math, clamp, Game, tween, Tween, Color, BoxCollider2D, UITransform, UIOpacity } from "cc";
|
||||
import { Vec3, _decorator, v3, Collider2D, Contact2DType, Node, Prefab, instantiate, tween, Tween, BoxCollider2D, UITransform } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { HeroSpine } from "./HeroSpine";
|
||||
import { HeroTopBarComp } from "./HeroTopBarComp";
|
||||
import { BoxSet, FacSet, FightSet, NumberFormatter, TooltipTypes } from "../common/config/GameSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { SkillSet, } from "../common/config/SkillSet";
|
||||
@@ -37,21 +38,10 @@ export class HeroViewComp extends CCComp {
|
||||
realDeadTime: number = 0.1
|
||||
deadCD: number = 0
|
||||
monDeadTime: number = 0.1
|
||||
hp_height: number = 90
|
||||
boss_hp_height: number = 160
|
||||
// 血条显示相关
|
||||
lastBarUpdateTime: number = 0; // 最后一次血条/蓝条/护盾更新时间
|
||||
// ==================== UI 节点引用 ====================
|
||||
private top_node: Node = null!;
|
||||
private topOpacity: UIOpacity = null!;
|
||||
private topBasePos: Vec3 = v3();
|
||||
private lvLabel: Label | null = null; // 等级显示文本
|
||||
private readonly barIdleOpacity: number = 153;
|
||||
private readonly barActiveOpacity: number = 255;
|
||||
private readonly idleOpacityDelay: number = 0.25;
|
||||
private readonly restoreBarIdleOpacity = () => {
|
||||
this.setTopBarOpacity(false);
|
||||
};
|
||||
private topBar: HeroTopBarComp = null!;
|
||||
|
||||
// ==================== 直接访问 HeroAttrsComp ====================
|
||||
get model() {
|
||||
@@ -96,67 +86,26 @@ export class HeroViewComp extends CCComp {
|
||||
this.lastBarUpdateTime = 0;
|
||||
this.status_change("idle");
|
||||
|
||||
// 初始化 UI 节点
|
||||
this.initUINodes();
|
||||
// 初始化头顶状态栏
|
||||
const topNode = this.node.getChildByName("top");
|
||||
this.topBar = topNode.getComponent(HeroTopBarComp) || topNode.addComponent(HeroTopBarComp);
|
||||
this.topBar.init(this.model, this.scale);
|
||||
|
||||
/** 方向 */
|
||||
this.node.setScale(this.scale * Math.abs(this.node.scale.x), 1 * this.node.scale.y); // 确保 scale.x 为正后再乘方向
|
||||
// top_node 取 abs 避免对象池复用时 scale 累乘导致血条方向反转
|
||||
this.top_node.setScale(this.scale * Math.abs(this.top_node.scale.x), 1 * this.top_node.scale.y);
|
||||
/* 显示角色血*/
|
||||
this.top_node.getChildByName("hp").active = true;
|
||||
this.top_node.getChildByName("cd").active = false;
|
||||
this.top_node.getChildByName("shield").active = false;
|
||||
this.top_node.getChildByName("lv").active = true;
|
||||
this.top_node.active = true;
|
||||
this.setTopBarOpacity(false);
|
||||
|
||||
// 初始化等级显示
|
||||
this.lv_show();
|
||||
|
||||
// 🔥 重置血条 UI 显示状态
|
||||
// 🔥 重置描边
|
||||
if (this.model) {
|
||||
this.hp_show();
|
||||
|
||||
// 根据英雄模型中的等级数据设置描边
|
||||
// 注意:HeroViewComp 是挂在 node 上的,而 FlashSprite 可能挂在子节点(如 Sprite/anm 节点)
|
||||
let flashSprite = this.node.getComponent(FlashSprite);
|
||||
if (!flashSprite) {
|
||||
// 如果当前节点没有,尝试在所有子节点中查找
|
||||
flashSprite = this.node.getComponentInChildren(FlashSprite);
|
||||
}
|
||||
|
||||
if (flashSprite) {
|
||||
flashSprite.setOutlineByLevel(this.model.lv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 UI 节点引用 */
|
||||
private initUINodes() {
|
||||
this.top_node = this.node.getChildByName("top");
|
||||
this.topOpacity = this.top_node.getComponent(UIOpacity) || this.top_node.addComponent(UIOpacity);
|
||||
this.top_node.setPosition(0, this.hp_height, 0);
|
||||
if (this.model.is_boss) this.top_node.setPosition(0, this.boss_hp_height, 0);
|
||||
this.topBasePos = this.top_node.position.clone();
|
||||
const hpNode = this.top_node.getChildByName("hp");
|
||||
if (this.model.fac == FacSet.HERO) {
|
||||
hpNode.getChildByName("Bar").getComponent(Sprite).color = new Color("#2ECC71")
|
||||
}
|
||||
|
||||
// 确保血条等UI始终在最上层显示
|
||||
this.top_node.setSiblingIndex(999);
|
||||
|
||||
// 缓存等级显示 Label(lv 节点下的 Label 子节点)
|
||||
const lvNode = this.top_node.getChildByName("lv");
|
||||
if (lvNode) {
|
||||
this.lvLabel = lvNode.getChildByName("Label")?.getComponent(Label) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* View 层每帧更新
|
||||
* 注意:数据更新逻辑已移到 HeroAttrSystem,这里只负责显示
|
||||
@@ -181,103 +130,21 @@ export class HeroViewComp extends CCComp {
|
||||
|
||||
// ✅ 按需更新 UI(脏标签模式)- 只在属性变化时更新
|
||||
if (this.model.dirty_hp) {
|
||||
this.hp_show();
|
||||
this.topBar.hpShow();
|
||||
this.model.dirty_hp = false;
|
||||
}
|
||||
|
||||
|
||||
if (this.model.dirty_shield) {
|
||||
this.show_shield(this.model.shield);
|
||||
this.topBar.shieldShow(this.model.shield);
|
||||
this.model.dirty_shield = false;
|
||||
}
|
||||
|
||||
if (this.model.dirty_lv) {
|
||||
this.lv_show();
|
||||
this.model.dirty_lv = false;
|
||||
}
|
||||
}
|
||||
|
||||
public cd_show() {
|
||||
return;
|
||||
}
|
||||
|
||||
/** 显示护盾 */
|
||||
private show_shield(shield: number = 0) {
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
this.node.getChildByName("shielded").active = shield > 0;
|
||||
}
|
||||
|
||||
/** 显示血量 */
|
||||
private hp_show() {
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
// 不再基于血量是否满来决定显示状态,只更新进度条
|
||||
let hp = this.model.hp;
|
||||
let hp_max = this.model.hp_max;
|
||||
// mLogger.log(this.debugMode, 'HeroViewComp', "hp_show",hp,hp_max)
|
||||
|
||||
let targetProgress = hp_max > 0 ? hp / hp_max : 0;
|
||||
targetProgress = clamp(targetProgress, 0, 1);
|
||||
let hpNode = this.top_node.getChildByName("hp");
|
||||
let hpProgressBar = hpNode.getComponent(ProgressBar);
|
||||
|
||||
const prevProgress = hpProgressBar.progress;
|
||||
if (targetProgress < hpProgressBar.progress) {
|
||||
this.activateTopBar();
|
||||
this.playHpBarShake();
|
||||
}
|
||||
hpProgressBar.progress = targetProgress;
|
||||
if (targetProgress > prevProgress) {
|
||||
this.setTopBarOpacity(false);
|
||||
}
|
||||
}
|
||||
|
||||
private isFullHpAndNoShield(): boolean {
|
||||
if (!this.model) return false;
|
||||
if (this.model.hp_max <= 0) return false;
|
||||
return this.model.hp >= this.model.hp_max;
|
||||
}
|
||||
|
||||
/** 更新等级显示 */
|
||||
private lv_show() {
|
||||
if (!this.lvLabel || !this.model) return;
|
||||
this.lvLabel.string = String(this.model.lv);
|
||||
}
|
||||
|
||||
private setTopBarOpacity(isActive: boolean) {
|
||||
if (!this.top_node || !this.top_node.isValid) return;
|
||||
if (this.topOpacity) {
|
||||
this.topOpacity.opacity = this.barActiveOpacity;
|
||||
}
|
||||
if (isActive) {
|
||||
this.top_node.active = true;
|
||||
return;
|
||||
}
|
||||
this.top_node.active = true;
|
||||
}
|
||||
|
||||
private activateTopBar() {
|
||||
this.setTopBarOpacity(true);
|
||||
this.unschedule(this.restoreBarIdleOpacity);
|
||||
this.scheduleOnce(this.restoreBarIdleOpacity, this.idleOpacityDelay);
|
||||
}
|
||||
|
||||
private playHpBarShake() {
|
||||
if (!this.top_node || !this.top_node.isValid) return;
|
||||
Tween.stopAllByTarget(this.top_node);
|
||||
this.top_node.setPosition(this.topBasePos);
|
||||
tween(this.top_node)
|
||||
.by(0.04, { position: v3(-3, 0, 0) })
|
||||
.by(0.04, { position: v3(6, 0, 0) })
|
||||
.by(0.04, { position: v3(-5, 0, 0) })
|
||||
.by(0.04, { position: v3(2, 0, 0) })
|
||||
.call(() => {
|
||||
this.top_node.setPosition(this.topBasePos);
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 升级特效 */
|
||||
public lv_up() {
|
||||
this.spawnTimedFx("game/skill/buff/buff_lvup", this.node, 1.0);
|
||||
@@ -349,7 +216,7 @@ export class HeroViewComp extends CCComp {
|
||||
Tooltip.load(pos, type, value, s_uuid, this.node.parent, 1, this.model?.fac ?? FacSet.MON);
|
||||
}
|
||||
|
||||
/** 护盾格挡提示 */
|
||||
/** 护盾减免提示 */
|
||||
shield_tip(absorbed: number) {
|
||||
this.hp_tip(TooltipTypes.shield, NumberFormatter.formatNumber(Math.max(0, Math.floor(absorbed))));
|
||||
}
|
||||
@@ -432,7 +299,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
add_shield(shield: number) {
|
||||
// 护盾数据更新由 Model 层处理,这里只负责视图表现
|
||||
if (this.model && this.model.shield > 0) this.show_shield(this.model.shield);
|
||||
if (this.model && this.model.shield > 0) this.topBar.shieldShow(this.model.shield);
|
||||
}
|
||||
|
||||
health(hp: number = 0) {
|
||||
@@ -458,7 +325,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
|
||||
// 恢复UI
|
||||
this.top_node.active = true;
|
||||
this.topBar.show();
|
||||
|
||||
this.status_change("idle");
|
||||
|
||||
@@ -504,7 +371,7 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
|
||||
// 隐藏UI
|
||||
this.top_node.active = false;
|
||||
this.topBar.hide();
|
||||
|
||||
// 在销毁实体前先禁用碰撞体,从源头减少"尸体"参与碰撞
|
||||
const collider = this.node.getComponent(Collider2D);
|
||||
@@ -516,23 +383,19 @@ export class HeroViewComp extends CCComp {
|
||||
let isHero = this.model.fac === FacSet.HERO;
|
||||
let dirX = isHero ? -800 : 800;
|
||||
|
||||
// 死亡往后飞出屏幕动画
|
||||
// 死亡往后飞出屏幕动画,结束后统一销毁实体
|
||||
// 英雄为真实死亡:快照已在 doDead 阶段写入 smc.mission.dead_heroes,此处直接销毁
|
||||
tween(this.node)
|
||||
.by(0.5, { position: v3(dirX, 700, 0) }, { easing: "quadOut" })
|
||||
.call(() => {
|
||||
if (isHero) {
|
||||
// 将英雄移到玩家看不到的墓地,留待下回合上场
|
||||
this.node.setPosition(v3(-2000, -2000, 0));
|
||||
} else {
|
||||
// 动画结束后销毁怪物实体
|
||||
this.ent.destroy();
|
||||
}
|
||||
this.ent.destroy();
|
||||
})
|
||||
.start();
|
||||
}
|
||||
do_atked(damage: number, isCrit: boolean, s_uuid: number, isBack: boolean = false) {
|
||||
/** 受击表现(伤害数字、受击动画、击退) */
|
||||
do_atked(damage: number, isCrit: boolean, s_uuid: number, knockbackExtra: number = 0) {
|
||||
// 受到攻击时更新最后更新时间
|
||||
this.activateTopBar();
|
||||
this.topBar.activateTopBar();
|
||||
this.lastBarUpdateTime = Date.now() / 1000;
|
||||
|
||||
if (damage <= 0) return;
|
||||
@@ -547,43 +410,15 @@ export class HeroViewComp extends CCComp {
|
||||
// 视图层表现
|
||||
let SConf = SkillSet[s_uuid]
|
||||
const hitAnm = SConf?.DAnm || "atked";
|
||||
if (isBack) this.back()
|
||||
this.back(knockbackExtra)
|
||||
this.in_atked(hitAnm, this.model.fac == FacSet.HERO ? 1 : -1);
|
||||
this.showDamage(damage, isCrit);
|
||||
}
|
||||
|
||||
private isBackingUp: boolean = false; // 🔥 添加后退状态标记
|
||||
|
||||
//后退
|
||||
back(distance: number = 0) {
|
||||
// 🔥 防止重复调用后退动画
|
||||
if (this.isBackingUp) return;
|
||||
this.isBackingUp = true; // 🔥 设置后退状态
|
||||
|
||||
if (this.model.fac == FacSet.MON) {
|
||||
// 基础击退距离,加上额外的距离强化
|
||||
let dist = FightSet.BACK_RANG + distance;
|
||||
let tx = this.node.position.x + dist;
|
||||
if (tx > 320) tx = 320;
|
||||
tween(this.node)
|
||||
.to(0.1, { position: v3(tx, this.node.position.y, 0) })
|
||||
.call(() => {
|
||||
this.isBackingUp = false; // 🔥 动画完成后重置状态
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
if (this.model.fac == FacSet.HERO) {
|
||||
let dist = 5 + distance;
|
||||
let tx = this.node.position.x - dist;
|
||||
if (tx < -320) tx = -320;
|
||||
tween(this.node)
|
||||
.to(0.1, { position: v3(tx, this.node.position.y, 0) })
|
||||
.call(() => {
|
||||
this.isBackingUp = false; // 🔥 动画完成后重置状态
|
||||
})
|
||||
.start();
|
||||
}
|
||||
// 击退位移由 MoveSystem 统一处理,视图层只负责触发,不直接操作 position
|
||||
// 具体击退速度由 HeroAtkSystem 在伤害结算后写入 MoveComp.knockbackVelocity
|
||||
}
|
||||
// 伤害计算和战斗逻辑已迁移到 HeroBattleSystem
|
||||
|
||||
@@ -634,7 +469,7 @@ export class HeroViewComp extends CCComp {
|
||||
if (!this.model) return;
|
||||
|
||||
const damageText = NumberFormatter.formatNumber(Math.max(0, Math.floor(damage)));
|
||||
this.hp_show();
|
||||
this.topBar.hpShow();
|
||||
if (isCrit) {
|
||||
this.hp_tip(TooltipTypes.crit, damageText);
|
||||
} else {
|
||||
@@ -645,10 +480,7 @@ export class HeroViewComp extends CCComp {
|
||||
// 清理残留的定时器和缓动
|
||||
this.unscheduleAllCallbacks();
|
||||
Tween.stopAllByTarget(this.node);
|
||||
if (this.top_node && this.top_node.isValid) {
|
||||
Tween.stopAllByTarget(this.top_node);
|
||||
this.top_node.setPosition(this.topBasePos);
|
||||
}
|
||||
this.topBar.reset();
|
||||
|
||||
// 清理碰撞器事件监听
|
||||
const collider = this.getComponent(Collider2D);
|
||||
@@ -657,7 +489,6 @@ export class HeroViewComp extends CCComp {
|
||||
}
|
||||
this.deadCD = 0
|
||||
this.lastBarUpdateTime = 0
|
||||
this.unschedule(this.restoreBarIdleOpacity);
|
||||
|
||||
// 清理伤害队列
|
||||
this.damageQueue.length = 0;
|
||||
|
||||
@@ -3,13 +3,12 @@ import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/O
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
|
||||
import { HeroInfo, resolveTriggerByLv, resolveReviveByLv } from "../common/config/heroSet";
|
||||
import { HeroInfo, resolveTriggerByLv, resolveReviveByLv, HeroDisVal } from "../common/config/heroSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
import { MonMoveComp } from "./MonMoveComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { SkillTriggerType } from "../common/config/heroSet";
|
||||
import { SkillTriggerType, HType } from "../common/config/heroSet";
|
||||
import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||
/** 怪物实体:负责怪物对象池复用、属性初始化、入场动画与回收 */
|
||||
@ecs.register(`Monster`)
|
||||
@@ -19,7 +18,7 @@ export class Monster extends ecs.Entity {
|
||||
/** 怪物表现组件引用 */
|
||||
HeroView!: HeroViewComp;
|
||||
/** 怪物移动组件引用 */
|
||||
MonMove!: MonMoveComp;
|
||||
MonMove!: MoveComp;
|
||||
/** 调试开关,控制生命周期日志输出 */
|
||||
private debugMode: boolean = false;
|
||||
|
||||
@@ -98,7 +97,7 @@ export class Monster extends ecs.Entity {
|
||||
/** 注册实体必需组件:移动 + 属性 */
|
||||
protected init() {
|
||||
this.addComponents<ecs.Comp>(
|
||||
MonMoveComp,
|
||||
MoveComp,
|
||||
HeroAttrsComp,
|
||||
);
|
||||
}
|
||||
@@ -149,8 +148,9 @@ export class Monster extends ecs.Entity {
|
||||
(node as any)._baseScale = node.scale.clone();
|
||||
}
|
||||
|
||||
// 统一挂到实体显示层 HERO 节点下
|
||||
node.parent = scene.entityLayer!.node!.getChildByName("HERO")!;
|
||||
// 按身份挂载显示层:Boss → BOSS 层(最底),小怪 → MON 层(编辑器已配置好层级顺序)
|
||||
const layerName = is_boss ? "BOSS" : "MON";
|
||||
node.parent = scene.entityLayer!.node!.getChildByName(layerName)!;
|
||||
var view = node.getComponent(HeroViewComp)!;
|
||||
const collider = node.getComponent(BoxCollider2D);
|
||||
// 入场期间关闭碰撞,防止下落时提前参与战斗
|
||||
@@ -175,7 +175,8 @@ export class Monster extends ecs.Entity {
|
||||
model.speed = hero.speed ?? 800;
|
||||
model.type = hero.type;
|
||||
model.fac = FacSet.MON;
|
||||
model.dis = hero.dis ?? 720;
|
||||
// 攻击距离:配置优先;未配置按攻击定位取默认值(近战100 / 中程300 / 远程450)
|
||||
model.dis = hero.dis ?? HeroDisVal[hero.type as HType.Melee | HType.Mid | HType.Long];
|
||||
model.posIndex = laneIndex;
|
||||
if (laneIndex >= 0) {
|
||||
smc.mission.monGrid[laneIndex] = this.eid;
|
||||
@@ -185,7 +186,6 @@ export class Monster extends ecs.Entity {
|
||||
model.call = hero.call;
|
||||
model.dead = hero.dead;
|
||||
model.fstart = hero.fstart;
|
||||
model.fend = hero.fend;
|
||||
model.atking = hero.atking;
|
||||
model.atked = hero.atked;
|
||||
model.revive = hero.revive;
|
||||
@@ -224,7 +224,6 @@ export class Monster extends ecs.Entity {
|
||||
if (testSkills.atked !== undefined) model.atked = testSkills.atked;
|
||||
if (testSkills.dead !== undefined) model.dead = testSkills.dead;
|
||||
if (testSkills.fstart !== undefined) model.fstart = testSkills.fstart;
|
||||
if (testSkills.fend !== undefined) model.fend = testSkills.fend;
|
||||
if (testSkills.call !== undefined) model.call = testSkills.call;
|
||||
if (testSkills.revive !== undefined) model.revive = testSkills.revive;
|
||||
}
|
||||
@@ -233,7 +232,6 @@ export class Monster extends ecs.Entity {
|
||||
model.runtime_call = resolveTriggerByLv(model.call, mon_lv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, mon_lv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, mon_lv);
|
||||
model.runtime_fend = resolveTriggerByLv(model.fend, mon_lv);
|
||||
model.runtime_atking = resolveTriggerByLv(model.atking, mon_lv);
|
||||
model.runtime_atked = resolveTriggerByLv(model.atked, mon_lv);
|
||||
model.runtime_revive = resolveReviveByLv(model.revive, mon_lv);
|
||||
@@ -248,7 +246,7 @@ export class Monster extends ecs.Entity {
|
||||
// 广播怪物加载事件,供刷怪与战斗系统联动
|
||||
oops.message.dispatchEvent("monster_load", this)
|
||||
// 初始化移动参数:方向、目标 X、站位基准 Y
|
||||
const move = this.get(MonMoveComp);
|
||||
const move = this.get(MoveComp);
|
||||
move.reset();
|
||||
move.direction = -1;
|
||||
move.targetX = pos.x;
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { BoxSet, FacSet } from "../common/config/GameSet";
|
||||
import { Node } from "cc";
|
||||
|
||||
@ecs.register('MonMoveComp')
|
||||
export class MonMoveComp extends ecs.Comp {
|
||||
/** 朝向:1=向右,-1=向左 */
|
||||
direction: number = -1;
|
||||
/** 当前移动目标 X */
|
||||
targetX: number = 0;
|
||||
/** 是否允许移动(出生落地前会短暂关闭) */
|
||||
moving: boolean = true;
|
||||
/** 站位基准 Y */
|
||||
baseY: number = 0;
|
||||
/** 出生序,用于同条件渲染排序稳定 */
|
||||
spawnOrder: number = 0;
|
||||
|
||||
reset() {
|
||||
this.direction = -1;
|
||||
this.targetX = 0;
|
||||
this.moving = true;
|
||||
this.baseY = 0;
|
||||
this.spawnOrder = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ecs.register('MonMoveSystem')
|
||||
export class MonMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||
/** 怪物接近英雄后的停船距离:小于该阈值即停下交战 */
|
||||
private readonly combatHaltDistance = 80;
|
||||
/** 渲染层级重排节流,避免每帧排序 */
|
||||
private readonly renderSortInterval = 0.05;
|
||||
private lastRenderSortAt = 0;
|
||||
private monMoveMatcher: ecs.IMatcher | null = null;
|
||||
private heroViewMatcher: ecs.IMatcher | null = null;
|
||||
private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number }[] = [];
|
||||
private renderEntryCount = 0;
|
||||
|
||||
private getMonMoveMatcher(): ecs.IMatcher {
|
||||
if (!this.monMoveMatcher) {
|
||||
this.monMoveMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp, MonMoveComp);
|
||||
}
|
||||
return this.monMoveMatcher;
|
||||
}
|
||||
|
||||
private getHeroViewMatcher(): ecs.IMatcher {
|
||||
if (!this.heroViewMatcher) {
|
||||
this.heroViewMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp);
|
||||
}
|
||||
return this.heroViewMatcher;
|
||||
}
|
||||
|
||||
filter(): ecs.IMatcher {
|
||||
return ecs.allOf(MonMoveComp, HeroViewComp, HeroAttrsComp);
|
||||
}
|
||||
|
||||
update(e: ecs.Entity) {
|
||||
/** 战斗未开始/暂停时不驱动移动 */
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
|
||||
const model = e.get(HeroAttrsComp);
|
||||
const move = e.get(MonMoveComp);
|
||||
const view = e.get(HeroViewComp);
|
||||
if (!model || !move || !view || !view.node) return;
|
||||
if (model.fac !== FacSet.MON) return;
|
||||
if (!move.moving) return;
|
||||
|
||||
/** 关卡阶段性冻结怪物行为 */
|
||||
if (smc.mission.stop_mon_action) {
|
||||
this.clearCombatTarget(model);
|
||||
view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost() || model.isStun()) {
|
||||
this.clearCombatTarget(model);
|
||||
if (!model.is_reviving) view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
/** 所有移动都锁定在 baseY,避免出现“漂移” */
|
||||
if (view.node.position.y !== move.baseY) {
|
||||
view.node.setPosition(view.node.position.x, move.baseY, 0);
|
||||
}
|
||||
// 渲染层级统交由 MoveSystem 统一处理,避免两个 System 争抢 setSiblingIndex
|
||||
|
||||
// 仅在战斗中才处理索敌;非战斗阶段也允许向左推进,让怪物自然逼近英雄阵地
|
||||
const nearestEnemy = smc.mission.in_fight ? this.findNearestEnemy(e) : null;
|
||||
if (nearestEnemy) {
|
||||
/** 有敌人:边移动边攻击 */
|
||||
this.processCombatLogic(e, move, view, model, nearestEnemy);
|
||||
this.syncCombatTarget(model, view, nearestEnemy);
|
||||
} else {
|
||||
/** 无敌人:清目标并继续向左推进 */
|
||||
this.clearCombatTarget(model);
|
||||
model.is_atking = false;
|
||||
this.moveLeft(view, move, model);
|
||||
}
|
||||
}
|
||||
|
||||
private clearCombatTarget(model: HeroAttrsComp): void {
|
||||
model.combat_target_eid = -1;
|
||||
model.enemy_in_cast_range = false;
|
||||
}
|
||||
|
||||
private syncCombatTarget(model: HeroAttrsComp, selfView: HeroViewComp, enemyView: HeroViewComp): void {
|
||||
if (!enemyView || !enemyView.node || !enemyView.ent) {
|
||||
this.clearCombatTarget(model);
|
||||
return;
|
||||
}
|
||||
const enemyAttrs = enemyView.ent.get(HeroAttrsComp);
|
||||
if (!enemyAttrs || enemyAttrs.is_dead || enemyAttrs.is_reviving || enemyAttrs.fac === model.fac) {
|
||||
this.clearCombatTarget(model);
|
||||
return;
|
||||
}
|
||||
model.combat_target_eid = enemyView.ent.eid;
|
||||
model.enemy_in_cast_range = this.isEnemyInAttackRange(model, selfView.node.position.x, enemyView.node.position.x);
|
||||
}
|
||||
|
||||
private isEnemyInAttackRange(model: HeroAttrsComp, selfX: number, enemyX: number): boolean {
|
||||
const dist = Math.abs(selfX - enemyX);
|
||||
const attackRange = model.dis;
|
||||
return dist <= attackRange;
|
||||
}
|
||||
|
||||
private processCombatLogic(e: ecs.Entity, move: MonMoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
const selfX = view.node.position.x;
|
||||
const enemyX = enemy.node.position.x;
|
||||
// 停船判定使用固定阈值(80),不依赖技能攻击范围
|
||||
const inRange = Math.abs(selfX - enemyX) <= this.combatHaltDistance;
|
||||
|
||||
if (inRange) {
|
||||
// 接触到英雄方:停止移动,进入攻击状态,由 SCastSystem 负责技能释放
|
||||
model.is_atking = true;
|
||||
const dir = enemyX > selfX ? 1 : -1;
|
||||
view.scale = dir;
|
||||
if (view.status === "move") {
|
||||
view.status_change("idle");
|
||||
}
|
||||
} else {
|
||||
// 未接触:继续向左推进
|
||||
model.is_atking = false;
|
||||
this.moveLeft(view, move, model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 持续向左推进:怪物不再固定站位,而是以 speed/3 的速度向左移动,
|
||||
* 直到抵达左边界(基地位置)后转入待机,由 SCastSystem 继续负责技能释放。
|
||||
*/
|
||||
private moveLeft(view: HeroViewComp, move: MonMoveComp, model: HeroAttrsComp) {
|
||||
const currentX = view.node.position.x;
|
||||
if (currentX <= BoxSet.LETF_END) {
|
||||
if (view.status !== "atk") {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const dir = -1;
|
||||
move.direction = dir;
|
||||
const speed = model.speed / 3;
|
||||
const delta = speed * this.dt * dir;
|
||||
const newX = Math.max(BoxSet.LETF_END, currentX + delta);
|
||||
if (Math.abs(newX - currentX) >= 0.01) {
|
||||
view.node.setPosition(newX, view.node.position.y, 0);
|
||||
view.status_change("move");
|
||||
}
|
||||
}
|
||||
|
||||
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
|
||||
const currentView = entity.get(HeroViewComp);
|
||||
if (!currentView?.node) return null;
|
||||
|
||||
const currentPos = currentView.node.position;
|
||||
const myFac = entity.get(HeroAttrsComp).fac;
|
||||
|
||||
let nearest: HeroViewComp | null = null;
|
||||
let minDis = Infinity;
|
||||
|
||||
/** 遍历筛出最近敌人:以 X 轴距离为主,Y 轴距离作为同排的决胜权重,使角色优先攻击同路的敌人 */
|
||||
ecs.query(this.getHeroViewMatcher()).forEach(e => {
|
||||
const m = e.get(HeroAttrsComp);
|
||||
if (m.fac !== myFac && !m.is_dead) {
|
||||
const v = e.get(HeroViewComp);
|
||||
if (v?.node) {
|
||||
const dx = Math.abs(currentPos.x - v.node.position.x);
|
||||
const dy = Math.abs(currentPos.y - v.node.position.y);
|
||||
const d = dx + dy * 0.1; // Y轴权重较小,仅在 X 相近时起决定作用
|
||||
if (d < minDis) {
|
||||
minDis = d;
|
||||
nearest = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return nearest;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +1,55 @@
|
||||
/**
|
||||
* @file MoveComp.ts
|
||||
* @description 统一移动组件与系统(英雄 + 怪物)
|
||||
*
|
||||
* 核心规则:
|
||||
* 1. 准备阶段:双方原地待命,不推进不索敌。
|
||||
* 2. 战斗阶段:双方向最近敌人推进,进入攻击范围(model.dis)后停止移动并攻击。
|
||||
* 3. 场上无敌人:停止移动(战斗结束,由 MissionComp 进入结算)。
|
||||
* 4. 击退/位移不会把单位推出屏幕:HERO 不越过 BoxSet.LETF_END,MON 不越过 BoxSet.RIGHT_END。
|
||||
* 5. 推进方向无上限限制,直到接触敌人或抵达敌方边界。
|
||||
*/
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { BoxSet, FacSet } from "../common/config/GameSet";
|
||||
import { HeroDisVal, HType, HRole } from "../common/config/heroSet";
|
||||
import { BoxCollider2D, Node } from "cc";
|
||||
import { MonMoveComp } from "./MonMoveComp";
|
||||
import { Node } from "cc";
|
||||
|
||||
@ecs.register('MoveComp')
|
||||
export class MoveComp extends ecs.Comp {
|
||||
/** 朝向:1=向右,-1=向左 */
|
||||
/** 朝向:1=向右(HERO),-1=向左(MON),由系统根据 fac 自动维护 */
|
||||
direction: number = 1;
|
||||
/** 当前移动目标 X(战斗/回位都会更新) */
|
||||
/** 当前移动目标 X */
|
||||
targetX: number = 0;
|
||||
/** 是否允许移动(出生落地前会短暂关闭) */
|
||||
moving: boolean = true;
|
||||
/** 预留:目标 Y(当前逻辑主要使用 baseY 锁定地平线) */
|
||||
targetY: number = 0;
|
||||
/** 角色所属车道的地平线 Y,移动时强制贴地 */
|
||||
/** 站位基准 Y,移动时强制贴地 */
|
||||
baseY: number = 0;
|
||||
/** 预留:车道索引 */
|
||||
lane: number = 0;
|
||||
/** 出生序,用于同条件渲染排序稳定 */
|
||||
spawnOrder: number = 0;
|
||||
/** 击退速度(像素/秒),受击瞬间由 HeroAtkSystem 赋值,MoveSystem 每帧指数衰减至 0 */
|
||||
knockbackVelocity: number = 0;
|
||||
|
||||
reset() {
|
||||
this.direction = 1;
|
||||
this.targetX = 0;
|
||||
this.moving = true;
|
||||
this.targetY = 0;
|
||||
this.baseY = 0;
|
||||
this.lane = 0;
|
||||
this.spawnOrder = 0;
|
||||
this.knockbackVelocity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
interface MoveFacConfig {
|
||||
/** 阵营可前进边界(靠近敌方一侧) */
|
||||
moveFrontX: number;
|
||||
/** 阵营可后退边界(靠近己方一侧) */
|
||||
moveBackX: number;
|
||||
/** 被逼退时前侧安全边界 */
|
||||
retreatFrontX: number;
|
||||
/** 被逼退时后侧安全边界 */
|
||||
retreatBackX: number;
|
||||
}
|
||||
|
||||
@ecs.register('MoveSystem')
|
||||
export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
|
||||
private readonly heroFrontAnchorX = -200;
|
||||
private readonly monFrontAnchorX = 0;
|
||||
/** 常规同阵营横向最小间距(英雄) */
|
||||
private readonly heroAllySpacingX = 100;
|
||||
/** 常规同阵营横向最小间距(怪物) */
|
||||
private readonly monAllySpacingX = 75;
|
||||
/** 纵向判定为同排的最大 Y 差 */
|
||||
private readonly minSpacingY = 30;
|
||||
/** 渲染层级重排节流,避免每帧排序 */
|
||||
private readonly renderSortInterval = 0.05;
|
||||
private lastRenderSortAt = 0;
|
||||
private heroMoveMatcher: ecs.IMatcher | null = null;
|
||||
private heroViewMatcher: ecs.IMatcher | null = null;
|
||||
private readonly renderEntries: { node: Node; bossPriority: number; frontScore: number; spawnOrder: number; eid: number; laneScore: number }[] = [];
|
||||
private renderEntryCount = 0;
|
||||
|
||||
/**
|
||||
* 阵营可移动边界配置。
|
||||
* HERO 在左侧出生并向右推进;MON 在右侧出生并向左推进。
|
||||
*/
|
||||
private readonly facConfigs: Record<number, MoveFacConfig> = {
|
||||
[FacSet.HERO]: {
|
||||
moveFrontX: 999999,
|
||||
moveBackX: -999999,
|
||||
retreatFrontX: 999999,
|
||||
retreatBackX: -999999,
|
||||
},
|
||||
[FacSet.MON]: {
|
||||
moveFrontX: -999999,
|
||||
moveBackX: 999999,
|
||||
retreatFrontX: -999999,
|
||||
retreatBackX: 999999,
|
||||
}
|
||||
};
|
||||
|
||||
private getHeroMoveMatcher(): ecs.IMatcher {
|
||||
if (!this.heroMoveMatcher) {
|
||||
this.heroMoveMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp, MoveComp);
|
||||
}
|
||||
return this.heroMoveMatcher;
|
||||
}
|
||||
|
||||
private getHeroViewMatcher(): ecs.IMatcher {
|
||||
if (!this.heroViewMatcher) {
|
||||
this.heroViewMatcher = ecs.allOf(HeroAttrsComp, HeroViewComp);
|
||||
@@ -109,52 +69,63 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
const move = e.get(MoveComp);
|
||||
const view = e.get(HeroViewComp);
|
||||
if (!model || !move || !view || !view.node) return;
|
||||
if (model.fac !== FacSet.HERO) return; // 只处理英雄移动
|
||||
if (!move.moving) return;
|
||||
|
||||
if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost()) {
|
||||
/** 关卡阶段性冻结怪物行为 */
|
||||
if (smc.mission.stop_mon_action && model.fac === FacSet.MON) {
|
||||
this.clearCombatTarget(model);
|
||||
view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
/** 死亡/复活/控制状态:停止移动与索敌 */
|
||||
if (model.is_stop || model.is_dead || model.is_reviving || model.isFrost() || model.isStun()) {
|
||||
this.clearCombatTarget(model);
|
||||
if (!model.is_reviving) view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 获取全局排位目标
|
||||
const slot = this.getGlobalFormationSlot(e, model);
|
||||
move.baseY = slot.targetY;
|
||||
move.targetX = slot.targetX;
|
||||
|
||||
// 2. 平滑 Y 轴换路(不分阶段,立即响应编队变化)
|
||||
let isChangingLane = false;
|
||||
if (Math.abs(view.node.position.y - move.baseY) > 2) {
|
||||
const currentY = view.node.position.y;
|
||||
const deltaY = move.baseY - currentY;
|
||||
const step = 400 * this.dt; // 换路速度
|
||||
const newY = currentY + Math.sign(deltaY) * Math.min(Math.abs(deltaY), step);
|
||||
view.node.setPosition(view.node.position.x, newY, 0);
|
||||
isChangingLane = true;
|
||||
} else {
|
||||
/** 所有移动都锁定在 baseY,避免出现"漂移" */
|
||||
if (view.node.position.y !== move.baseY) {
|
||||
view.node.setPosition(view.node.position.x, move.baseY, 0);
|
||||
}
|
||||
|
||||
// 渲染层级重排
|
||||
this.updateRenderOrder();
|
||||
/** 击退位移:每帧叠加 knockbackVelocity 并指数衰减,与推进解耦 */
|
||||
if (Math.abs(move.knockbackVelocity) > 0.01) {
|
||||
const newX = view.node.position.x + move.knockbackVelocity * this.dt;
|
||||
const clampedX = this.clampByFacBoundary(newX, model.fac);
|
||||
if (Math.abs(clampedX - view.node.position.x) > 0.01) {
|
||||
view.node.setPosition(clampedX, view.node.position.y, 0);
|
||||
}
|
||||
// 时间常数 τ=0.1s:0.1 秒衰减到约 37%,0.3 秒衰减到约 5%,总位移 ≈ v0 * 0.0632
|
||||
move.knockbackVelocity *= Math.exp(-10 * this.dt);
|
||||
if (Math.abs(move.knockbackVelocity) < 0.01) {
|
||||
move.knockbackVelocity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** 准备阶段:双方原地待命,不推进不索敌 */
|
||||
if (!smc.mission.in_fight) {
|
||||
this.clearCombatTarget(model);
|
||||
model.is_atking = false;
|
||||
if (view.status !== "atk") view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
|
||||
/** 战斗阶段:索敌并推进 */
|
||||
const nearestEnemy = this.findNearestEnemy(e);
|
||||
if (nearestEnemy) {
|
||||
/** 有敌人:进入战斗位移逻辑(立即向编队目标移动) */
|
||||
this.processCombatLogic(e, move, view, model, nearestEnemy);
|
||||
this.syncCombatTarget(model, view, nearestEnemy);
|
||||
} else {
|
||||
/** 无敌人:清目标并回归编队站位 */
|
||||
/** 无敌人:停止移动,等待战斗结束结算 */
|
||||
this.clearCombatTarget(model);
|
||||
this.moveToSlot(view, move, model, move.targetX);
|
||||
model.is_atking = false;
|
||||
if (view.status !== "atk") view.status_change("idle");
|
||||
}
|
||||
|
||||
// 如果只在 Y 轴移动,也要播放 move 动画
|
||||
if (isChangingLane && view.status !== "move" && view.status !== "atk") {
|
||||
view.status_change("move");
|
||||
}
|
||||
/** 渲染层级重排(双阵营统一处理) */
|
||||
this.updateRenderOrder();
|
||||
}
|
||||
|
||||
private clearCombatTarget(model: HeroAttrsComp): void {
|
||||
@@ -182,232 +153,55 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
return dist <= attackRange;
|
||||
}
|
||||
|
||||
private processCombatLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
const rangeType = model.type as HType.Melee | HType.Mid | HType.Long;
|
||||
switch (rangeType) {
|
||||
case HType.Melee:
|
||||
this.processMeleeLogic(e, move, view, model, enemy);
|
||||
break;
|
||||
case HType.Mid:
|
||||
this.processMidLogic(e, move, view, model, enemy);
|
||||
break;
|
||||
case HType.Long:
|
||||
this.processLongLogic(e, move, view, model, enemy);
|
||||
break;
|
||||
default:
|
||||
this.processMidLogic(e, move, view, model, enemy); // 默认中程
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 近战交战:有怪物时向右推进,贴近到固定接触距离后停下攻击。
|
||||
* Why: 近战手短需主动逼近;停止判定用固定接触距离(非攻击距离 dis),
|
||||
* 使英雄进入攻击范围后仍继续贴近,直到贴上目标才站稳输出。
|
||||
* 怪物 y 与英雄同在 GAME_LINE 附近(微调也在攻击范围内),无需管 Y 轴。
|
||||
* 战斗位移:向最近敌人推进,进入攻击范围后停止。
|
||||
* 推进无上限限制,但击退/位移受阵营后退边界保护。
|
||||
*/
|
||||
private processMeleeLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
if (!enemy?.node) {
|
||||
this.processFormationCombat(e, move, view, model);
|
||||
return;
|
||||
}
|
||||
private processCombatLogic(_e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
const selfX = view.node.position.x;
|
||||
const enemyX = enemy.node.position.x;
|
||||
const dist = Math.abs(selfX - enemyX);
|
||||
// 已贴近到固定接触距离:停下攻击
|
||||
if (dist <= MoveSystem.MELEE_ENGAGE_X) {
|
||||
const attackRange = model.dis;
|
||||
/** 近战贴身作战:攻击距离 dis(100) 大于技能范围(60),推进到与敌人相隔 60 才停下,保证技能可命中且不过度贴身 */
|
||||
const stopRange = model.type === 0 ? 60 : attackRange;
|
||||
const inRange = dist <= stopRange;
|
||||
|
||||
if (inRange) {
|
||||
/** 进入攻击范围:停止移动,进入攻击状态,由 SCastSystem 负责技能释放 */
|
||||
model.is_atking = true;
|
||||
if (view.status !== "atk") view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
// 未贴近:向怪物方向推进(攻击范围内也继续移动),于接触距离处停下
|
||||
const dir = enemyX > selfX ? 1 : -1;
|
||||
move.direction = dir;
|
||||
const stopX = enemyX - dir * MoveSystem.MELEE_ENGAGE_X;
|
||||
const speed = model.speed / 3;
|
||||
this.moveEntity(view, dir, speed, stopX);
|
||||
model.is_atking = false;
|
||||
}
|
||||
|
||||
private processMidLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
this.processFormationCombat(e, move, view, model);
|
||||
}
|
||||
|
||||
private processLongLogic(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp, enemy: HeroViewComp) {
|
||||
this.processFormationCombat(e, move, view, model);
|
||||
}
|
||||
|
||||
private processFormationCombat(e: ecs.Entity, move: MoveComp, view: HeroViewComp, model: HeroAttrsComp) {
|
||||
this.moveToSlot(view, move, model, move.targetX);
|
||||
model.is_atking = true;
|
||||
}
|
||||
|
||||
private getGlobalFormationSlot(self: ecs.Entity, model: HeroAttrsComp): { targetX: number, targetY: number } {
|
||||
const allAllies: ecs.Entity[] = [];
|
||||
ecs.query(this.getHeroMoveMatcher()).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
const view = e.get(HeroViewComp);
|
||||
const move = e.get(MoveComp);
|
||||
if (!attrs || !view?.node || !move) return;
|
||||
if (attrs.is_dead || attrs.is_reviving) return;
|
||||
if (attrs.fac !== model.fac) return;
|
||||
allAllies.push(e);
|
||||
});
|
||||
|
||||
allAllies.sort((a, b) => {
|
||||
const attrsA = a.get(HeroAttrsComp);
|
||||
const attrsB = b.get(HeroAttrsComp);
|
||||
const priorityA = attrsA ? this.getFormationPriority(attrsA) : 0;
|
||||
const priorityB = attrsB ? this.getFormationPriority(attrsB) : 0;
|
||||
if (priorityA !== priorityB) return priorityB - priorityA;
|
||||
|
||||
const lvA = attrsA?.lv ?? 1;
|
||||
const lvB = attrsB?.lv ?? 1;
|
||||
if (lvA !== lvB) return lvB - lvA;
|
||||
const moveA = a.get(MoveComp);
|
||||
const moveB = b.get(MoveComp);
|
||||
const orderA = moveA?.spawnOrder ?? 0;
|
||||
const orderB = moveB?.spawnOrder ?? 0;
|
||||
if (orderA !== orderB) return orderA - orderB;
|
||||
return a.eid - b.eid;
|
||||
});
|
||||
|
||||
// allAllies 按优先级降序(索引 0 = 最高优先级 = 最靠右/最先接敌),
|
||||
// 反转为"从左侧起的排位"后再分配 X:最高优先级 → 最右
|
||||
const priorityRank = Math.max(0, allAllies.findIndex(entity => entity === self));
|
||||
const rankFromLeft = allAllies.length - 1 - priorityRank;
|
||||
const targetX = this.calcSlotX(rankFromLeft, allAllies.length);
|
||||
|
||||
return { targetX, targetY: BoxSet.GAME_LINE };
|
||||
}
|
||||
|
||||
// ==================== 编队站位(方案 A:单行横向分布) ====================
|
||||
/** 编队左端起点(最靠后/最后接敌) */
|
||||
private static readonly FORMATION_LEFT_X = -320;
|
||||
/** 编队右端终点(最靠前/最先接敌) */
|
||||
private static readonly FORMATION_RIGHT_X = 100;
|
||||
/** 固定步进间距(人数较少时按此从左侧逐位右排) */
|
||||
private static readonly FORMATION_STEP_X = 60;
|
||||
/** 近战贴近怪物的固定接触距离:小于攻击距离 dis,使英雄进攻击范围后仍继续贴近到此间距才停 */
|
||||
private static readonly MELEE_ENGAGE_X = 60;
|
||||
|
||||
/**
|
||||
* 计算某个槽位的目标 X。
|
||||
* Why: 单行站位避免横版拥挤——人少时按 60 固定步进从 -320 起步逐位右排(保持松散),
|
||||
* 人多到 60 步进装不下时,改为在 [-320, 100] 区间均分(利用全宽)。
|
||||
* @param rankFromLeft 从左侧起的排位(0=最靠左/最后,越大越靠右/越先接敌)
|
||||
* @param total 当前存活英雄总数
|
||||
*/
|
||||
private calcSlotX(rankFromLeft: number, total: number): number {
|
||||
const left = MoveSystem.FORMATION_LEFT_X;
|
||||
const right = MoveSystem.FORMATION_RIGHT_X;
|
||||
const step = MoveSystem.FORMATION_STEP_X;
|
||||
if (total <= 1) return left;
|
||||
// 固定步进能容纳的最大人数:相邻 60,且最右不超过 right
|
||||
const maxStepCount = Math.floor((right - left) / step) + 1;
|
||||
if (total <= maxStepCount) {
|
||||
return left + rankFromLeft * step;
|
||||
}
|
||||
// 超出后均分整段区间(含左右端点)
|
||||
return left + (right - left) * (rankFromLeft / (total - 1));
|
||||
}
|
||||
|
||||
private moveToSlot(view: HeroViewComp, move: MoveComp, model: HeroAttrsComp, targetX: number) {
|
||||
const currentX = view.node.position.x;
|
||||
const currentY = view.node.position.y;
|
||||
|
||||
// 当 X 和 Y 都到达目标时,才算真正到达
|
||||
if (Math.abs(currentX - targetX) <= 2 && Math.abs(currentY - move.baseY) <= 2) {
|
||||
view.node.setPosition(targetX, move.baseY, 0);
|
||||
view.status_change("idle");
|
||||
return;
|
||||
}
|
||||
const dir = targetX > currentX ? 1 : -1;
|
||||
move.direction = dir;
|
||||
const speed = model.speed / 3;
|
||||
this.moveEntity(view, dir, speed, targetX);
|
||||
}
|
||||
|
||||
private moveEntity(view: HeroViewComp, direction: number, speed: number, stopAtX?: number) {
|
||||
const model = view.ent.get(HeroAttrsComp);
|
||||
const move = view.ent.get(MoveComp);
|
||||
if (!model || !move) return;
|
||||
/** 按阵营边界裁剪,防止跑出战场可移动区域 */
|
||||
const cfg = this.facConfigs[model.fac] || this.facConfigs[FacSet.HERO];
|
||||
const moveMinX = Math.min(cfg.moveBackX, cfg.moveFrontX);
|
||||
const moveMaxX = Math.max(cfg.moveBackX, cfg.moveFrontX);
|
||||
const currentX = view.node.position.x;
|
||||
const currentY = view.node.position.y;
|
||||
|
||||
const delta = speed * this.dt * direction;
|
||||
let newX = view.node.position.x + delta;
|
||||
if (currentX < moveMinX && direction < 0) {
|
||||
// X 轴到底了,如果 Y 轴还在换路,继续维持 move 状态
|
||||
if (Math.abs(currentY - move.baseY) > 2) {
|
||||
view.status_change("move");
|
||||
} else {
|
||||
const dir = enemyX > selfX ? 1 : -1;
|
||||
view.scale = dir;
|
||||
if (view.status === "move") {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (currentX > moveMaxX && direction > 0) {
|
||||
if (Math.abs(currentY - move.baseY) > 2) {
|
||||
} else {
|
||||
/** 未接触:向敌人方向推进 */
|
||||
model.is_atking = false;
|
||||
const dir = enemyX > selfX ? 1 : -1;
|
||||
move.direction = dir;
|
||||
const speed = model.speed / 3;
|
||||
const delta = speed * this.dt * dir;
|
||||
let newX = selfX + delta;
|
||||
|
||||
/** 后退边界钳制:防止被击退/位移推出屏幕 */
|
||||
newX = this.clampByFacBoundary(newX, model.fac);
|
||||
|
||||
if (Math.abs(newX - selfX) >= 0.01) {
|
||||
view.node.setPosition(newX, view.node.position.y, 0);
|
||||
view.status_change("move");
|
||||
} else {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
newX = Math.max(moveMinX, Math.min(moveMaxX, newX));
|
||||
if (stopAtX !== undefined) {
|
||||
/** 指定停止点时,限制不越过 stopAtX */
|
||||
newX = direction > 0 ? Math.min(newX, stopAtX) : Math.max(newX, stopAtX);
|
||||
}
|
||||
if (Math.abs(newX - currentX) < 0.01) {
|
||||
// X轴虽然没变化,但如果Y轴还在移动,依然是move状态
|
||||
if (Math.abs(currentY - move.baseY) > 2) {
|
||||
view.status_change("move");
|
||||
} else {
|
||||
view.status_change("idle");
|
||||
}
|
||||
return;
|
||||
}
|
||||
view.node.setPosition(newX, view.node.position.y, 0); // 注意:这里只更新 X,Y 在外部平滑逻辑更新
|
||||
view.status_change("move");
|
||||
}
|
||||
|
||||
/**
|
||||
* 编队站位优先级(数值越大越靠前/越靠右)。
|
||||
* Why: 阵容职能决定承伤顺序——死亡触发英雄需最先阵亡以触发遗志,
|
||||
* 其次坦克承伤,再次战士/刺客,远程射手/法师/辅助依次靠后保护。
|
||||
* 排序档位:死亡触发 > 坦克 > 战士 > 刺客 > 射手 > 法师 > 辅助
|
||||
* 按阵营裁剪 X 坐标,防止被击退/位移推出屏幕。
|
||||
* HERO 后退不越过 BoxSet.LETF_END;MON 后退不越过 BoxSet.RIGHT_END。
|
||||
*/
|
||||
private getFormationPriority(model: HeroAttrsComp): number {
|
||||
// 死亡触发英雄最高优先:需要先死以发动遗志/献祭
|
||||
if (model.dead && Object.keys(model.dead).length > 0) return 100;
|
||||
if (model.role !== undefined) {
|
||||
switch (model.role) {
|
||||
case HRole.Tank: return 90;
|
||||
case HRole.Warrior: return 80;
|
||||
case HRole.Assassin: return 70;
|
||||
case HRole.Archer: return 60;
|
||||
case HRole.Mage: return 50;
|
||||
case HRole.Support: return 40;
|
||||
}
|
||||
private clampByFacBoundary(x: number, fac: number): number {
|
||||
if (fac === FacSet.HERO) {
|
||||
return Math.max(BoxSet.LETF_END, x);
|
||||
}
|
||||
// 兜底:未配置 role 时按攻击定位(近战 > 中程 > 远程)
|
||||
const rangeType = model.type as HType.Melee | HType.Mid | HType.Long;
|
||||
if (rangeType === HType.Melee) return 30;
|
||||
if (rangeType === HType.Mid) return 20;
|
||||
return 10;
|
||||
}
|
||||
|
||||
private resolveCombatRange(model: HeroAttrsComp, defaultMin: number, defaultMax: number): [number, number] {
|
||||
const minRange = model.getCachedMinSkillDistance();
|
||||
const maxRange = model.getCachedMaxSkillDistance();
|
||||
if (maxRange <= 0) return [defaultMin, defaultMax];
|
||||
const safeMin = Math.max(0, Math.min(minRange, maxRange - 20));
|
||||
return [safeMin, maxRange];
|
||||
return Math.min(BoxSet.RIGHT_END, x);
|
||||
}
|
||||
|
||||
private findNearestEnemy(entity: ecs.Entity): HeroViewComp | null {
|
||||
@@ -441,13 +235,19 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
|
||||
private updateRenderOrder() {
|
||||
const scene = smc.map?.MapView?.scene;
|
||||
const actorRoot = scene?.entityLayer?.node?.getChildByName("HERO");
|
||||
if (!actorRoot) return;
|
||||
const layerNode = scene?.entityLayer?.node;
|
||||
if (!layerNode) return;
|
||||
|
||||
const now = Date.now() / 1000;
|
||||
if (now - this.lastRenderSortAt < this.renderSortInterval) return;
|
||||
this.lastRenderSortAt = now;
|
||||
|
||||
const heroRoot = layerNode.getChildByName("HERO");
|
||||
const monRoot = layerNode.getChildByName("MON");
|
||||
const bossRoot = layerNode.getChildByName("BOSS");
|
||||
|
||||
/** 分层重排同层节点:各阵营层内按 Y 轴路次 -> 前后位置 -> 出生序 -> eid 排序
|
||||
* (Boss 已有独立 BOSS 层固定在最底,无需 bossPriority 参与排序) */
|
||||
this.renderEntryCount = 0;
|
||||
ecs.query(this.getHeroViewMatcher()).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
@@ -455,24 +255,23 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
if (!attrs || !actorView?.node || attrs.is_dead) return;
|
||||
if (attrs.fac !== FacSet.HERO && attrs.fac !== FacSet.MON) return;
|
||||
|
||||
if (actorView.node.parent !== actorRoot) {
|
||||
actorView.node.parent = actorRoot;
|
||||
/** 按阵营/身份归位到对应显示层 */
|
||||
let targetRoot: Node | null | undefined;
|
||||
if (attrs.fac === FacSet.HERO) {
|
||||
targetRoot = heroRoot;
|
||||
} else {
|
||||
targetRoot = attrs.is_boss ? (bossRoot ?? monRoot ?? heroRoot) : (monRoot ?? heroRoot);
|
||||
}
|
||||
if (!targetRoot) return;
|
||||
if (actorView.node.parent !== targetRoot) {
|
||||
actorView.node.parent = targetRoot;
|
||||
}
|
||||
|
||||
// 获取 spawnOrder,由于可能挂载 MoveComp 或 MonMoveComp,我们需要动态获取
|
||||
let spawnOrder = 0;
|
||||
const heroMove = e.get(MoveComp);
|
||||
if (heroMove) {
|
||||
spawnOrder = heroMove.spawnOrder;
|
||||
} else {
|
||||
const monMove = e.get(MonMoveComp);
|
||||
if (monMove) {
|
||||
spawnOrder = monMove.spawnOrder;
|
||||
}
|
||||
}
|
||||
/** 统一从 MoveComp 获取 spawnOrder */
|
||||
const move = e.get(MoveComp);
|
||||
const spawnOrder = move?.spawnOrder ?? 0;
|
||||
|
||||
// 按 Y 轴计算渲染层级权重(Y 越小,说明越靠近屏幕下方,应该渲染在越前面)
|
||||
// 之前的 isFly 逻辑已被移除,统一按 Y 轴处理三路渲染
|
||||
const laneScore = -actorView.node.position.y;
|
||||
// X 轴权重:站在前排的(交战处)优先渲染
|
||||
const frontScore = attrs.fac === FacSet.HERO ? actorView.node.position.x : -actorView.node.position.x;
|
||||
@@ -484,7 +283,6 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
this.renderEntries.push(entry);
|
||||
}
|
||||
entry.node = actorView.node;
|
||||
entry.bossPriority = attrs.is_boss ? 1 : 0;
|
||||
entry.laneScore = laneScore;
|
||||
entry.frontScore = frontScore;
|
||||
entry.spawnOrder = spawnOrder;
|
||||
@@ -493,9 +291,7 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
});
|
||||
this.renderEntries.length = this.renderEntryCount;
|
||||
|
||||
/** 定时重排同层节点:Boss 优先级 -> Y 轴路次 -> 前后位置 -> 出生序 -> eid */
|
||||
this.renderEntries.sort((a, b) => {
|
||||
if (a.bossPriority !== b.bossPriority) return a.bossPriority - b.bossPriority;
|
||||
// Y轴靠下的(laneScore 较大)应该在后面,从而渲染在上面
|
||||
if (Math.abs(a.laneScore - b.laneScore) > 10) return a.laneScore - b.laneScore;
|
||||
if (a.frontScore !== b.frontScore) return a.frontScore - b.frontScore;
|
||||
@@ -503,8 +299,14 @@ export class MoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
return a.eid - b.eid;
|
||||
});
|
||||
|
||||
this.renderEntries.forEach((item, index) => {
|
||||
/** 分层应用 siblingIndex:各层内部从 0 递增 */
|
||||
const layerCounters = new Map<Node, number>();
|
||||
this.renderEntries.forEach((item) => {
|
||||
const parent = item.node.parent;
|
||||
if (!parent) return;
|
||||
const index = layerCounters.get(parent) ?? 0;
|
||||
item.node.setSiblingIndex(index);
|
||||
layerCounters.set(parent, index + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,6 @@ export class SkillTriggerHelper {
|
||||
case SkillTriggerType.FStart:
|
||||
this.handleArrayTrigger(model.runtime_fstart, model, view, type);
|
||||
break;
|
||||
case SkillTriggerType.FEnd:
|
||||
this.handleArrayTrigger(model.runtime_fend, model, view, type);
|
||||
break;
|
||||
case SkillTriggerType.Atking:
|
||||
this.handleAtking(model, view);
|
||||
break;
|
||||
@@ -142,7 +139,7 @@ export class SkillTriggerHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的数组型触发器处理(适用于 FStart / FEnd 等无需额外判定的简单列表触发)
|
||||
* 通用的数组型触发器处理(适用于 FStart 等无需额外判定的简单列表触发)
|
||||
*/
|
||||
private static handleArrayTrigger(configs: { s_uuid: number, t_num: number, overrides?: SkillOverrides }[] | undefined, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
|
||||
if (!configs || configs.length === 0) return;
|
||||
|
||||
@@ -478,7 +478,6 @@ export class CHeroComp extends CCComp {
|
||||
call: hero.call,
|
||||
dead: hero.dead,
|
||||
fstart: hero.fstart,
|
||||
fend: hero.fend,
|
||||
atking: hero.atking,
|
||||
atked: hero.atked,
|
||||
field: hero.field,
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每回合次数)
|
||||
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
|
||||
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
|
||||
* - FightEnd (5):监听 FightEnd 事件(每回合结束派发),按 trigger_limit 全局累计上限
|
||||
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
|
||||
* - HeroCall (7):监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
@@ -145,7 +144,6 @@ export class EquipBoxComp extends CCComp {
|
||||
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onFightStart, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.MasterCalled, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.ReviveSuccess, this.onEventTrigger, this);
|
||||
@@ -287,7 +285,6 @@ export class EquipBoxComp extends CCComp {
|
||||
* - Interval: 监听 FightStart,进入战斗后由 update 帧驱动计时
|
||||
* - Field: 不主动施法(实际生效由 FieldSkillSet 处理)
|
||||
* - FightStart: 监听 FightStart 事件
|
||||
* - FightEnd: 监听 FightEnd 事件
|
||||
* - HeroDead: 监听 HeroDead 事件(已在派发处做阵营过滤)
|
||||
* - HeroCall: 监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
@@ -313,10 +310,6 @@ export class EquipBoxComp extends CCComp {
|
||||
oops.message.on(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.FightEnd:
|
||||
oops.message.on(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.HeroDead:
|
||||
oops.message.on(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
break;
|
||||
@@ -338,7 +331,7 @@ export class EquipBoxComp extends CCComp {
|
||||
* 事件型触发的统一入口:
|
||||
* - Instant 类型:按 trigger_times 上限判定,复用 current_trigger_times 跟踪
|
||||
* (保持与原 is_instant 行为一致,且 NewWave 中也用 current_trigger_times)
|
||||
* - 事件型(FightStart/FightEnd/HeroDead/HeroCall):按 trigger_limit 上限判定,使用 trigger_count 跟踪
|
||||
* - 事件型(FightStart/HeroDead/HeroCall):按 trigger_limit 上限判定,使用 trigger_count 跟踪
|
||||
*
|
||||
* 注意:本方法不读取事件 payload,仅作触发信号使用(避免 MasterCalled 不同 payload 字段引发的兼容问题)。
|
||||
*/
|
||||
@@ -445,7 +438,6 @@ export class EquipBoxComp extends CCComp {
|
||||
const showRemainCount =
|
||||
this.trigger_type === CardTriggerType.Interval ||
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
|
||||
@@ -454,7 +446,6 @@ export class EquipBoxComp extends CCComp {
|
||||
// 事件型按 trigger_limit;Interval 按 t_times
|
||||
const isEvent =
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
const used = isEvent ? this.trigger_count : this.current_trigger_times;
|
||||
@@ -514,7 +505,7 @@ export class EquipBoxComp extends CCComp {
|
||||
* 处理维持回合逻辑:递减剩余回合,或者重置触发次数。
|
||||
*
|
||||
* 各类型在新一回合的行为:
|
||||
* - Instant/Interval/FightStart/FightEnd:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
|
||||
* - Instant/Interval/FightStart:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
|
||||
* - Field:被动生效,跟随 keep_waves 决定存活
|
||||
* - HeroDead/HeroCall:跨回合触发的事件型,trigger_count(全局)不重置,仅 keep_waves 控制存活
|
||||
*/
|
||||
|
||||
@@ -217,7 +217,6 @@ export class HeroBoxComp extends CCComp {
|
||||
call: hero.call,
|
||||
dead: hero.dead,
|
||||
fstart: hero.fstart,
|
||||
fend: hero.fend,
|
||||
atking: hero.atking,
|
||||
atked: hero.atked,
|
||||
field: hero.field,
|
||||
|
||||
@@ -61,10 +61,10 @@ export class HeroMoreCom extends Component {
|
||||
/** 冰冻率附加值 */
|
||||
@property(Label)
|
||||
private freeze_plus_label: Label = null;
|
||||
/** 击退率总值(百分点) */
|
||||
/** 击退强化总值(击退距离增量) */
|
||||
@property(Label)
|
||||
private knockback_all_label: Label = null;
|
||||
/** 击退率附加值 */
|
||||
/** 击退强化附加值 */
|
||||
@property(Label)
|
||||
private knockback_plus_label: Label = null;
|
||||
/** 穿透率总值(百分点) */
|
||||
@@ -177,12 +177,13 @@ export class HeroMoreCom extends Component {
|
||||
const finalCritDmg = this.model.getFinalCritDamage();
|
||||
this.setStatRow(this.crit_dmg_all_label, this.crit_dmg_plus_label, finalCritDmg, finalCritDmg - (this.model.crit_damage ?? 0), "%");
|
||||
|
||||
// ---- 冰冻率 / 击退率 / 穿透率 / 风怒率(百分点) ----
|
||||
// ---- 冰冻率 / 击退强化 / 穿透率 / 风怒率 ----
|
||||
const finalFreeze = this.model.getFinalFreezeChance();
|
||||
this.setStatRow(this.freeze_all_label, this.freeze_plus_label, finalFreeze, finalFreeze - (this.model.freeze_chance ?? 0), "%");
|
||||
|
||||
// 击退强化已改为击退距离增量,不再按百分比显示
|
||||
const finalKnockback = this.model.getFinalKnockbackChance();
|
||||
this.setStatRow(this.knockback_all_label, this.knockback_plus_label, finalKnockback, finalKnockback - (this.model.knockback_chance ?? 0), "%");
|
||||
this.setStatRow(this.knockback_all_label, this.knockback_plus_label, finalKnockback, finalKnockback - (this.model.knockback_chance ?? 0), "");
|
||||
|
||||
const finalPuncture = this.model.getFinalPunctureChance();
|
||||
this.setStatRow(this.puncture_all_label, this.puncture_plus_label, finalPuncture, finalPuncture - (this.model.puncture_chance ?? 0), "%");
|
||||
|
||||
@@ -192,13 +192,10 @@ export class MissionCardComp extends CCComp {
|
||||
private itemCardComps: ItemListComp[] = [];
|
||||
/** 是否已召唤过英雄(用于控制其他面板按钮是否可点击) */
|
||||
private hasCalledHero: boolean = false;
|
||||
/**
|
||||
* 招募计数:每次点击招募 +1。
|
||||
* 节奏为「每 4 次一循环」:前 3 次直接随机召唤,第 4 次走三选一。
|
||||
*/
|
||||
private recruitCount: number = 0;
|
||||
/** 全远程阵容时近战卡的权重放大倍数(近战保底) */
|
||||
private static readonly MELEE_PITY_WEIGHT_MUL = 3;
|
||||
/** 开局免费连续三选一的总次数 */
|
||||
private static readonly OPENING_FREE_PICK_COUNT = 3;
|
||||
/** 开局免费三选一剩余次数(>0 时招募不扣费,选完 1 张自动弹出下一组) */
|
||||
private openingFreePicks: number = 0;
|
||||
|
||||
// ======================== 面板划出状态 ========================
|
||||
|
||||
@@ -359,7 +356,12 @@ export class MissionCardComp extends CCComp {
|
||||
this.updatePanelButtonsInteractable();
|
||||
|
||||
// 初始化英雄信息盒(3 个空槽位)
|
||||
this.refreshHeroBoxSlots();
|
||||
// 暂不启用 herobox 同步显示英雄信息(后续可能恢复),保留代码备查
|
||||
// this.refreshHeroBoxSlots();
|
||||
|
||||
// 重置开局免费三选一次数,随后立即弹出第 1 组三选一
|
||||
this.openingFreePicks = MissionCardComp.OPENING_FREE_PICK_COUNT;
|
||||
this.showCardsPanel();
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "mission start");
|
||||
}
|
||||
@@ -753,19 +755,22 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
|
||||
// 英雄登场后同步英雄信息盒
|
||||
this.refreshHeroBoxSlots();
|
||||
// 暂不启用 herobox 同步(后续可能恢复)
|
||||
// this.refreshHeroBoxSlots();
|
||||
}
|
||||
|
||||
/** 英雄死亡事件回调:刷新面板列表并更新英雄数量 UI */
|
||||
private onHeroDead() {
|
||||
this.updateHeroNumUI(true, false);
|
||||
this.refreshHeroBoxSlots();
|
||||
// 暂不启用 herobox 同步(后续可能恢复)
|
||||
// this.refreshHeroBoxSlots();
|
||||
}
|
||||
|
||||
/** 英雄被出售事件回调:更新英雄数量 UI 并清空对应英雄信息盒 */
|
||||
private onHeroSell() {
|
||||
this.updateHeroNumUI(true, false);
|
||||
this.refreshHeroBoxSlots();
|
||||
// 暂不启用 herobox 同步(后续可能恢复)
|
||||
// this.refreshHeroBoxSlots();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -789,7 +794,8 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
/** 英雄升级事件回调:刷新英雄信息盒显示 */
|
||||
private onHeroLvUp() {
|
||||
this.refreshHeroBoxSlots();
|
||||
// 暂不启用 herobox 同步(后续可能恢复)
|
||||
// this.refreshHeroBoxSlots();
|
||||
}
|
||||
|
||||
/** 英雄面板空槽位点击回调:展示英雄卡池选择界面 */
|
||||
@@ -871,8 +877,18 @@ export class MissionCardComp extends CCComp {
|
||||
if (heroIdx >= 0) {
|
||||
const cards = this.buildDrawCards();
|
||||
this.dispatchCardsToSlots(cards);
|
||||
// 抽 1 张后关闭英雄卡池面板
|
||||
this.hideCardsPanel();
|
||||
// 开局免费三选一次数未用完时保持面板展开,连续弹出下一组三选一
|
||||
if (this.openingFreePicks > 0) {
|
||||
this.openingFreePicks--;
|
||||
if (this.openingFreePicks <= 0) {
|
||||
// 最后一组选完,免费次数耗尽,收起面板并通知自动进入战斗
|
||||
this.hideCardsPanel();
|
||||
oops.message.dispatchEvent("OpeningPicksDone");
|
||||
}
|
||||
} else {
|
||||
// 抽 1 张后关闭英雄卡池面板
|
||||
this.hideCardsPanel();
|
||||
}
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "refresh hero pool after buy", {
|
||||
triggerSlot: heroIdx
|
||||
});
|
||||
@@ -1221,40 +1237,22 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 英雄招募按钮(每 4 次一循环):
|
||||
* - 前 3 次:扣面板开启费后直接随机召唤 1 个英雄(不开面板,无需玩家选)。
|
||||
* - 第 4 次:扣费后展开三选一面板(缩放 0→1),玩家选 1。
|
||||
* 英雄招募按钮:每次都展开三选一面板,玩家选 1;
|
||||
* 开局免费三选一次数未用完时不扣面板开启费。
|
||||
* 满员(HERO_MAX_NUM)时气泡提示并拦截,不扣费、不计数。
|
||||
*/
|
||||
private onShowHerosClick() {
|
||||
oops.audio.playEffect("music/button");
|
||||
// 满员拦截:随机招募与三选一都禁止
|
||||
// 满员拦截
|
||||
if (this.getAliveHeroCount() >= this.getMissionHeroMaxNum()) {
|
||||
this.showSmallTip("panel_hero_full", "英雄槽已满");
|
||||
return;
|
||||
}
|
||||
// 随机招募与三选一都需支付面板开启费,金币不足则提示并中止
|
||||
if (!this.tryPayPanelOpenCost(0)) return;
|
||||
|
||||
// 先计数,再按节奏分支:count % 4 == 0 展开三选一,其余直接随机召唤
|
||||
this.recruitCount++;
|
||||
const isPickOneOfThree = (this.recruitCount % 4) === 0;
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "hero recruit", {
|
||||
recruitCount: this.recruitCount,
|
||||
mode: isPickOneOfThree ? "pick_one_of_three" : "random_summon"
|
||||
});
|
||||
|
||||
// 收起装备/技能/药品覆盖面板,回到英雄面板
|
||||
// 开局免费次数内不扣面板开启费
|
||||
if (this.openingFreePicks <= 0 && !this.tryPayPanelOpenCost(0)) return;
|
||||
this.slideToPanel(0);
|
||||
|
||||
if (isPickOneOfThree) {
|
||||
// 第 4 次:展开英雄抽卡面板(缩放 0→1,点亮按钮 active 高亮)
|
||||
this.showCardsPanel();
|
||||
} else {
|
||||
// 前 3 次:直接随机召唤,不开面板
|
||||
this.randomSummonOne();
|
||||
}
|
||||
this.showCardsPanel();
|
||||
}
|
||||
|
||||
// ======================== 英雄信息盒(herosBoxNode) ========================
|
||||
@@ -1266,8 +1264,12 @@ export class MissionCardComp extends CCComp {
|
||||
* 3. 多余槽位清空数据并隐藏(默认全部隐藏,召唤一个激活一个)。
|
||||
*
|
||||
* Why: 英雄召唤 / 死亡 / 卖出 / 升级后调用,保证面板与场上一一对应。
|
||||
*
|
||||
* 备注:当前版本暂不启用 herobox 同步显示英雄信息(后续可能恢复),
|
||||
* 方法体已整体注释,所有调用点也已注释。恢复时取消方法体与调用点注释。
|
||||
*/
|
||||
private refreshHeroBoxSlots() {
|
||||
/* 暂不启用 herobox 同步,保留实现备查
|
||||
if (!this.herosBoxNode || !this.herosBoxNode.isValid) return;
|
||||
|
||||
this.ensureHeroBoxSlots();
|
||||
@@ -1298,6 +1300,7 @@ export class MissionCardComp extends CCComp {
|
||||
|
||||
// 列表高度只按可见槽位(存活英雄数)计算,空槽位已隐藏不再占位
|
||||
this.resizeListBox(this.herosBoxNode, actors.length);
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1802,60 +1805,11 @@ export class MissionCardComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接随机召唤 1 个英雄:
|
||||
* - 全英雄卡池按权重随机(不过滤已召唤英雄,配合二合一合成)。
|
||||
* - 近战保底:场上已有英雄且全部为远程/中程(无近战)时,近战卡权重放大。
|
||||
*/
|
||||
private randomSummonOne() {
|
||||
const pool = this.buildRandomSummonPool();
|
||||
if (pool.length === 0) return;
|
||||
const pick = this.weightedPick(pool);
|
||||
if (!pick) return;
|
||||
|
||||
mLogger.log(this.debugMode, "MissionCardComp", "random summon", { uuid: pick.uuid });
|
||||
oops.message.dispatchEvent(GameEvent.CallHero, {
|
||||
uuid: pick.uuid,
|
||||
hero_lv: 1,
|
||||
card_lv: pick.card_lv ?? 1
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建随机召唤卡池:全英雄卡,必要时按近战保底放大近战权重。
|
||||
* @returns 带运行时权重的卡配置数组(weight 已按保底调整)
|
||||
* 构建随机召唤卡池:全英雄卡。
|
||||
* @returns 卡配置数组
|
||||
*/
|
||||
private buildRandomSummonPool(): CardConfig[] {
|
||||
const heroCards = CardPoolList.filter(c => c.type === CardType.Hero);
|
||||
if (heroCards.length === 0) return [];
|
||||
|
||||
// 全远程判定:有存活英雄但没有任何近战 → 触发放大近战权重
|
||||
const meleeMul = this.isAllRangedFormation()
|
||||
? MissionCardComp.MELEE_PITY_WEIGHT_MUL
|
||||
: 1;
|
||||
if (meleeMul === 1) return heroCards;
|
||||
|
||||
return heroCards.map(c => {
|
||||
const hero = HeroInfo[c.uuid];
|
||||
const isMelee = hero && hero.type === HType.Melee;
|
||||
// 复制一份避免污染卡池原始 weight
|
||||
return isMelee ? { ...c, weight: (c.weight ?? 0) * meleeMul } : c;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前场上是否为「全远程阵容」:
|
||||
* 有至少 1 个存活英雄,且没有任何近战(Melee)英雄。
|
||||
*/
|
||||
private isAllRangedFormation(): boolean {
|
||||
let heroCount = 0;
|
||||
let meleeCount = 0;
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach((entity: ecs.Entity) => {
|
||||
const model = entity.get(HeroAttrsComp);
|
||||
if (!model || model.fac !== FacSet.HERO || model.is_dead) return;
|
||||
heroCount++;
|
||||
if (model.type === HType.Melee) meleeCount++;
|
||||
});
|
||||
return heroCount > 0 && meleeCount === 0;
|
||||
return CardPoolList.filter(c => c.type === CardType.Hero);
|
||||
}
|
||||
|
||||
// ======================== 阶段切换 ========================
|
||||
@@ -2310,7 +2264,6 @@ export class MissionCardComp extends CCComp {
|
||||
model.runtime_call = resolveTriggerByLv(model.call, nextLv);
|
||||
model.runtime_dead = resolveTriggerByLv(model.dead, nextLv);
|
||||
model.runtime_fstart = resolveTriggerByLv(model.fstart, nextLv);
|
||||
model.runtime_fend = resolveTriggerByLv(model.fend, nextLv);
|
||||
model.runtime_atking = resolveTriggerByLv(model.atking, nextLv);
|
||||
model.runtime_atked = resolveTriggerByLv(model.atked, nextLv);
|
||||
model.runtime_field = resolveFieldByLv(model.field, nextLv);
|
||||
|
||||
@@ -48,7 +48,7 @@ import { Tooltip } from "../skill/Tooltip";
|
||||
import { Timer } from "db://oops-framework/core/common/timer/Timer";
|
||||
import { FieldSkillType } from "../common/config/SkillSet";
|
||||
import { FieldSkillHelper } from "../hero/FieldSkillHelper";
|
||||
import { spawningEngine, MAX_WAVE, DynamicTuner, WAVE_TIMEOUT, WAVE_TIMEOUT_BOSS, BATCH_INTERVAL, BATCH_COUNT, WAVE_DURATION } from "./RogueConfig";
|
||||
import { spawningEngine, WAVE_DURATION, BATTLE_TIMEOUT } from "./RogueConfig";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
/** 任务(关卡)生命周期阶段 */
|
||||
@@ -82,11 +82,6 @@ export class MissionComp extends CCComp {
|
||||
|
||||
// ======================== 配置参数 ========================
|
||||
|
||||
/** 怪物数量上限(超过后暂停刷怪):略高于单回合上限 MAX_MONSTERS(54),只有跨回合堆积才触发泄压 */
|
||||
private maxMonsterCount: number = 60;
|
||||
/** 怪物数量恢复阈值(降至此值以下恢复刷怪,需留空间接纳后续批次,单批最大约 18 只) */
|
||||
private resumeMonsterCount: number = 40;
|
||||
|
||||
// ======================== 编辑器绑定节点 ========================
|
||||
|
||||
/** 开始战斗按钮 */
|
||||
@@ -196,8 +191,8 @@ export class MissionComp extends CCComp {
|
||||
this.on(GameEvent.NewWave, this.onNewWave, this)
|
||||
this.on(GameEvent.DO_AD_BACK, this.do_ad, this)
|
||||
this.start_btn?.on(NodeEventType.TOUCH_END, this.onStartFightBtnClick, this)
|
||||
// 第一回合准备阶段,首个英雄召唤后自动开始战斗(无需玩家点击开始按钮)
|
||||
oops.message.on(GameEvent.MasterCalled, this.onHeroSummoned, this);
|
||||
// 开局免费三选一选完后自动进入战斗
|
||||
oops.message.on("OpeningPicksDone", this.onOpeningPicksDone, this);
|
||||
this.removeMemoryPanel()
|
||||
}
|
||||
|
||||
@@ -219,7 +214,7 @@ export class MissionComp extends CCComp {
|
||||
if (this.start_btn && this.start_btn.isValid) {
|
||||
this.start_btn.off(NodeEventType.TOUCH_END, this.onStartFightBtnClick, this);
|
||||
}
|
||||
oops.message.off(GameEvent.MasterCalled, this.onHeroSummoned, this);
|
||||
oops.message.off("OpeningPicksDone", this.onOpeningPicksDone, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,7 +225,7 @@ export class MissionComp extends CCComp {
|
||||
protected update(dt: number): void {
|
||||
if (!smc.mission.play) return
|
||||
|
||||
// 如果是暂停状态,且不在 BattleEnd 阶段(全灭时需要播放完 fend 技能动画并自动流转),才真正停止 update 逻辑
|
||||
// 如果是暂停状态,且不在 BattleEnd 阶段(全灭时需要自动流转),才真正停止 update 逻辑
|
||||
if (smc.mission.pause && this.currentPhase !== MissionPhase.BattleEnd) return
|
||||
|
||||
// 回合间倒计时:PrepareStart 复用为倒计时阶段,5 秒后自动进入下一回合
|
||||
@@ -259,9 +254,8 @@ export class MissionComp extends CCComp {
|
||||
this.clearTime += dt
|
||||
this.update_time();
|
||||
|
||||
// 回合超时兜底:僵局(坦克 vs 高血 Boss 互磨不死)时强制收束回合,Boss 回合阈值放宽
|
||||
const timeout = this.isBossWave ? WAVE_TIMEOUT_BOSS : WAVE_TIMEOUT;
|
||||
if (this.clearTime >= timeout) {
|
||||
// 战斗超时兜底:僵局(坦克 vs 高血 Boss 互磨不死)时强制结算整局
|
||||
if (this.clearTime >= BATTLE_TIMEOUT) {
|
||||
this.onBattleTimeout();
|
||||
}
|
||||
}
|
||||
@@ -521,12 +515,12 @@ export class MissionComp extends CCComp {
|
||||
|
||||
case MissionPhase.BattleStart:
|
||||
// 触发战斗开始技能(fstart)
|
||||
this.triggerHeroBattleSkills(true);
|
||||
// Boss 回合预警:战斗正式开始、Boss 压轴进场(最后一批),提前给 UI 表现窗口
|
||||
this.triggerHeroBattleSkills();
|
||||
// Boss 预警:Boss 同步场立即登场(eta=0)
|
||||
if (this.isBossWave) {
|
||||
oops.message.dispatchEvent(GameEvent.BossWarning, {
|
||||
wave: this.currentWave,
|
||||
eta: BATCH_INTERVAL * (BATCH_COUNT - 1),
|
||||
eta: 0,
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -560,47 +554,31 @@ export class MissionComp extends CCComp {
|
||||
|
||||
let allAlive = true;
|
||||
let hasHero = false;
|
||||
let heroDeathCount = 0;
|
||||
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (attrs && attrs.fac === FacSet.HERO) {
|
||||
hasHero = true;
|
||||
if (attrs.is_dead) {
|
||||
allAlive = false;
|
||||
heroDeathCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
// 英雄为真实死亡(实体已销毁),本回合死亡数取死亡记录长度
|
||||
const heroDeathCount = smc.mission.dead_heroes.length;
|
||||
if (heroDeathCount > 0) allAlive = false;
|
||||
|
||||
// 【动态难度调节】根据本回合战况自动放水 / 加压
|
||||
// 清场加速节省的时间还原进口径,避免"打得好"被节奏加速+强度加压双重惩罚
|
||||
const effectiveClearTime = this.clearTime + smc.vmdata.mission_data.wave_early_skip;
|
||||
const tuned = DynamicTuner.adjust(effectiveClearTime, heroDeathCount);
|
||||
if (tuned) {
|
||||
mLogger.log(this.debugMode, 'MissionComp', `[DynamicTuner] wave=${this.currentWave} effClear=${effectiveClearTime.toFixed(1)}s deaths=${heroDeathCount} factor=${DynamicTuner.factor.toFixed(3)}`);
|
||||
}
|
||||
// 快照供下一回合倒计时档位判定(onNewWave 会清零 clearTime,必须提前快照)
|
||||
// 快照供倒计时档位判定(onNewWave 会清零 clearTime,必须提前快照)
|
||||
this.lastWaveDeathCount = heroDeathCount;
|
||||
this.lastWaveClearTime = effectiveClearTime;
|
||||
this.lastWaveClearTime = this.clearTime;
|
||||
// 【评分系统 - 战绩分】记录全员存活的胜利回合数(额外加分)
|
||||
if (hasHero && allAlive) {
|
||||
smc.vmdata.scores.wave_all_alive_count++;
|
||||
}
|
||||
|
||||
// 【评分系统 - 战绩分】判断是否通过最后一关(第20回合)
|
||||
if (this.currentWave === MAX_WAVE) {
|
||||
smc.vmdata.scores.passed_wave_20 = true;
|
||||
}
|
||||
// 【评分系统 - 战绩分】单场 Boss 战定胜负,通过即视为通关
|
||||
smc.vmdata.scores.passed_wave_20 = true;
|
||||
}
|
||||
|
||||
// 触发战斗结束技能(fend)
|
||||
this.triggerHeroBattleSkills(false);
|
||||
|
||||
// 战斗结束阶段,按 FightSet.WAVE_HEAL_RATE 恢复所有英雄血量
|
||||
this.healAllHeroes();
|
||||
|
||||
// 【新增】派发每回合战斗结束事件,供卡牌技能监听(区别于整局结束的 MissionEnd)
|
||||
oops.message.dispatchEvent(GameEvent.FightEnd);
|
||||
break;
|
||||
|
||||
case MissionPhase.Settle:
|
||||
@@ -686,32 +664,26 @@ export class MissionComp extends CCComp {
|
||||
* - 标记非战斗
|
||||
* - 暂停刷怪
|
||||
* - 显示开始按钮
|
||||
* - 触发英雄战斗结束技能
|
||||
*/
|
||||
private enterPreparePhase() {
|
||||
this.changePhase(MissionPhase.PrepareStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发英雄的战斗开始/结束技能
|
||||
* @param isStart 是否为战斗开始
|
||||
* 触发英雄的战斗开始技能
|
||||
*/
|
||||
private triggerHeroBattleSkills(isStart: boolean) {
|
||||
private triggerHeroBattleSkills() {
|
||||
let triggerCount = 1;
|
||||
if (isStart) {
|
||||
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.StartCount);
|
||||
} else {
|
||||
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.EndCount);
|
||||
}
|
||||
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.StartCount);
|
||||
triggerCount = Math.max(1, Math.floor(triggerCount));
|
||||
|
||||
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
const view = entity.get(HeroViewComp);
|
||||
if (!attrs || !view || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
|
||||
// 触发战斗开始/结束技能
|
||||
// 触发战斗开始技能
|
||||
for (let i = 0; i < triggerCount; i++) {
|
||||
SkillTriggerHelper.trigger(isStart ? SkillTriggerType.FStart : SkillTriggerType.FEnd, attrs, view);
|
||||
SkillTriggerHelper.trigger(SkillTriggerType.FStart, attrs, view);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -753,13 +725,8 @@ export class MissionComp extends CCComp {
|
||||
this.to_fight();
|
||||
}
|
||||
|
||||
/**
|
||||
* 英雄召唤事件回调:
|
||||
* 第一回合准备阶段(Prepare)只有第 1 回合才会进入,
|
||||
* 此时首个英雄一旦召唤即自动开战,无需玩家点击开始按钮。
|
||||
* 后续回合走 PrepareStart 倒计时自动流转,不会进入 Prepare。
|
||||
*/
|
||||
private onHeroSummoned() {
|
||||
/** 开局免费三选一选完,自动进入战斗(等效于点击开始按钮) */
|
||||
private onOpeningPicksDone() {
|
||||
if (this.currentPhase !== MissionPhase.Prepare) return;
|
||||
if (!smc.mission.play || smc.mission.pause) return;
|
||||
this.to_fight();
|
||||
@@ -777,8 +744,7 @@ export class MissionComp extends CCComp {
|
||||
open_Victory(e: any, is_hero_dead: boolean = false) {
|
||||
// 战斗失败或胜利,标记暂停状态以切断回合流转逻辑
|
||||
smc.mission.pause = true;
|
||||
// 直接切入 BattleEnd,触发 fend 表现
|
||||
// 倒计时逻辑已在 update 中由 PhaseTime 接管,2s 后将触发 autoNextPhase 弹窗结算
|
||||
// 直接切入 BattleEnd,2s 后由 autoNextPhase 自动流转弹窗结算
|
||||
this.changePhase(MissionPhase.BattleEnd);
|
||||
}
|
||||
|
||||
@@ -827,7 +793,6 @@ export class MissionComp extends CCComp {
|
||||
this.clearTime = 0
|
||||
smc.vmdata.mission_data.mon_num = 0
|
||||
smc.vmdata.mission_data.level = 1
|
||||
smc.vmdata.mission_data.mon_max = Math.max(1, Math.floor(this.maxMonsterCount))
|
||||
this.currentPhase = MissionPhase.None;
|
||||
this.currentWave = 1;
|
||||
this.isBossWave = false;
|
||||
@@ -879,10 +844,10 @@ export class MissionComp extends CCComp {
|
||||
const wave = Number(data?.wave ?? 0);
|
||||
if (wave <= 0) return;
|
||||
|
||||
this.isBossWave = !!data?.bossWave;
|
||||
this.isBossWave = true; // 单场 Boss 战,恒为 Boss 战(超时阈值/预警走 Boss 档)
|
||||
|
||||
if (wave > 1) {
|
||||
// 在新一回合到来时,先进入 BattleEnd,触发上一回合的战斗结束技能 (fend),2秒后自动进入下一回合的准备阶段
|
||||
// 在新一回合到来时,先进入 BattleEnd,2秒后自动进入下一回合的准备阶段
|
||||
this.changePhase(MissionPhase.BattleEnd);
|
||||
} else {
|
||||
// 第1回合不需要结束上一回合,延迟一点播放提示,避免被开始游戏的loading遮挡
|
||||
@@ -903,21 +868,10 @@ export class MissionComp extends CCComp {
|
||||
|
||||
// ======================== 怪物数量管理 ========================
|
||||
|
||||
/**
|
||||
* 获取怪物数量阈值配置。
|
||||
* @returns { max: 刷怪上限, resume: 恢复刷怪阈值 }
|
||||
*/
|
||||
private getMonsterThresholds(): { max: number; resume: number } {
|
||||
const max = Math.max(1, Math.floor(this.maxMonsterCount));
|
||||
const resume = Math.min(max - 1, Math.max(0, Math.floor(this.resumeMonsterCount)));
|
||||
return { max, resume };
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步怪物刷新状态(降频执行,每 0.2 秒一次):
|
||||
* 1. 遍历所有 HeroAttrsComp 实体,统计怪物和英雄数量。
|
||||
* 2. 检测英雄全灭。
|
||||
* 3. 根据 max/resume 阈值切换 stop_spawn_mon 状态。
|
||||
* 2. 检测英雄全灭 / 怪物全灭。
|
||||
*
|
||||
* @param dt 帧间隔
|
||||
*/
|
||||
@@ -940,53 +894,37 @@ export class MissionComp extends CCComp {
|
||||
});
|
||||
this.handleHeroWipe(heroCount);
|
||||
|
||||
// 怪物全灭检测:如果战斗阶段场上没有任何活着的怪物,且待刷新的怪物队列也为空,直接结束战斗进入下一回合的准备阶段
|
||||
// 怪物全灭检测:一场定胜负,全灭即整局通关,直接进入结算
|
||||
const pendingCount = smc.vmdata.mission_data.pending_mon_num || 0;
|
||||
if (monsterCount === 0 && pendingCount === 0 && smc.mission.play && !smc.mission.pause && this.currentPhase === MissionPhase.Battle) {
|
||||
if (this.currentWave >= MAX_WAVE) {
|
||||
// 20 回合通关
|
||||
this.open_Victory(null, false);
|
||||
} else {
|
||||
// oops.message 全局事件总线:清屏庆祝(横幅/奖励由 BattleBannerComp 消费),需在推进回合前派发(clearTime 归零前取值)
|
||||
const allAlive = this.checkAllHeroAlive();
|
||||
const fastClear = this.clearTime < WAVE_DURATION * 0.5 ? 2
|
||||
: this.clearTime < WAVE_DURATION * 0.75 ? 1 : 0;
|
||||
oops.message.dispatchEvent(GameEvent.WaveClear, {
|
||||
wave: this.currentWave,
|
||||
clearTime: this.clearTime,
|
||||
allAlive,
|
||||
fastClear,
|
||||
});
|
||||
oops.message.dispatchEvent("TimeUpAdvanceWave");
|
||||
}
|
||||
// oops.message 全局事件总线:清屏庆祝(横幅/奖励由 BattleBannerComp 消费),需在结算前派发(clearTime 归零前取值)
|
||||
const allAlive = this.checkAllHeroAlive();
|
||||
const fastClear = this.clearTime < WAVE_DURATION * 0.5 ? 2
|
||||
: this.clearTime < WAVE_DURATION * 0.75 ? 1 : 0;
|
||||
oops.message.dispatchEvent(GameEvent.WaveClear, {
|
||||
wave: this.currentWave,
|
||||
clearTime: this.clearTime,
|
||||
allAlive,
|
||||
fastClear,
|
||||
});
|
||||
this.open_Victory(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
smc.vmdata.mission_data.mon_num = monsterCount;
|
||||
const { max, resume } = this.getMonsterThresholds();
|
||||
smc.vmdata.mission_data.mon_max = max;
|
||||
const stopSpawn = !!smc.mission.stop_spawn_mon;
|
||||
if (stopSpawn) {
|
||||
// 降至恢复阈值以下 → 恢复刷怪
|
||||
if (monsterCount <= resume) smc.mission.stop_spawn_mon = false;
|
||||
return;
|
||||
}
|
||||
// 超过上限 → 暂停刷怪
|
||||
if (monsterCount >= max) smc.mission.stop_spawn_mon = true;
|
||||
}
|
||||
|
||||
/** 检测场上英雄是否全员存活(清屏 Perfect 判定用,独立于评分统计逻辑) */
|
||||
private checkAllHeroAlive(): boolean {
|
||||
let hasHero = false;
|
||||
let allAlive = true;
|
||||
ecs.query(this.heroAttrsMatcher).forEach(entity => {
|
||||
const attrs = entity.get(HeroAttrsComp);
|
||||
if (attrs && attrs.fac === FacSet.HERO) {
|
||||
hasHero = true;
|
||||
if (attrs.is_dead) allAlive = false;
|
||||
}
|
||||
});
|
||||
return hasHero && allAlive;
|
||||
// 英雄为真实死亡(实体已销毁),有死亡记录即非全员存活
|
||||
return hasHero && smc.mission.dead_heroes.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1001,13 +939,13 @@ export class MissionComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 回合超时强制结束(僵局兜底):
|
||||
* 战斗超时强制结束(僵局兜底):
|
||||
* 1. 预扣留存分(残留怪销毁后 mon_num 会被下一次同步清零,BattleEnd 的累加会漏扣,故在此显式预扣并置防重标志)。
|
||||
* 2. 销毁全部残留怪(不触发 MonDead、不掉金币——超时是对"清不掉"的惩罚)。
|
||||
* 3. 复用正常回合推进流;DynamicTuner 因 clearTime 远超阈值会自动放水,无需特殊处理。
|
||||
* 3. 一场定胜负,超时按怪物全灭同等处理——直接结算整局。
|
||||
*/
|
||||
private onBattleTimeout() {
|
||||
// 防重入:同一回合只触发一次
|
||||
// 防重入:只触发一次
|
||||
if (this.currentPhase !== MissionPhase.Battle) return;
|
||||
|
||||
smc.vmdata.scores.wave_remain_monsters += smc.vmdata.mission_data.mon_num;
|
||||
@@ -1021,13 +959,9 @@ export class MissionComp extends CCComp {
|
||||
});
|
||||
smc.vmdata.mission_data.mon_num = 0;
|
||||
|
||||
mLogger.log(this.debugMode, 'MissionComp', `[WaveTimeout] wave=${this.currentWave} 超时强制结束`);
|
||||
mLogger.log(this.debugMode, 'MissionComp', `[BattleTimeout] 超时强制结算`);
|
||||
|
||||
if (this.currentWave >= MAX_WAVE) {
|
||||
this.open_Victory(null, false);
|
||||
} else {
|
||||
oops.message.dispatchEvent("TimeUpAdvanceWave");
|
||||
}
|
||||
this.open_Victory(null, false);
|
||||
}
|
||||
|
||||
// ======================== 清理 ========================
|
||||
@@ -1059,21 +993,17 @@ export class MissionComp extends CCComp {
|
||||
this.clearBattleSceneNodes();
|
||||
}
|
||||
|
||||
/** 清理战斗场景中的 HERO 和 SKILL 根节点下的所有子节点 */
|
||||
/** 清理战斗场景中的 HERO/MON/BOSS 和 SKILL 根节点下的所有子节点 */
|
||||
private clearBattleSceneNodes() {
|
||||
const scene = smc.map?.MapView?.scene;
|
||||
const layer = scene?.entityLayer?.node;
|
||||
if (!layer) return;
|
||||
const heroRoot = layer.getChildByName("HERO");
|
||||
const skillRoot = layer.getChildByName("SKILL");
|
||||
if (heroRoot) {
|
||||
for (let i = heroRoot.children.length - 1; i >= 0; i--) {
|
||||
heroRoot.children[i].destroy();
|
||||
}
|
||||
}
|
||||
if (skillRoot) {
|
||||
for (let i = skillRoot.children.length - 1; i >= 0; i--) {
|
||||
skillRoot.children[i].destroy();
|
||||
for (const name of ["HERO", "MON", "BOSS", "SKILL"]) {
|
||||
const root = layer.getChildByName(name);
|
||||
if (root) {
|
||||
for (let i = root.children.length - 1; i >= 0; i--) {
|
||||
root.children[i].destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
* 旧英雄移动合并销毁,新英雄以 **两者最高等级 + 1** 落地(封顶 HERO_MAX_LV),
|
||||
* 等级已达上限时拒绝召唤。英雄也可通过升级卡(SpecialUpgrade)升级。
|
||||
*
|
||||
* 死亡机制:
|
||||
* 英雄为真实死亡(HeroAtkSystem.doDead → 快照至 smc.mission.dead_heroes → 实体销毁),
|
||||
* 本组件不做回合自动复活;复活由后续复活机制读取 dead_heroes 重新召唤。
|
||||
* revive 系技能为"濒死回血"(血量归零时消耗次数回血续命),不触发真死。
|
||||
*
|
||||
* 依赖:
|
||||
* - Hero(hero/Hero.ts)—— 英雄 ECS 实体类
|
||||
* - HeroAttrsComp —— 英雄属性组件
|
||||
@@ -48,14 +53,16 @@ const { ccclass } = _decorator;
|
||||
export class MissionHeroComp extends CCComp {
|
||||
// ======================== 常量 ========================
|
||||
|
||||
/** 硬编码的6个英雄占位点 */
|
||||
/**
|
||||
* 英雄登场占位点(固定 3 列):
|
||||
* - index 0/1/2 分别对应 x=-320/-260/-200。
|
||||
* - 分配规则:近战优先靠前(x 更大),远程/中程优先靠后(x 更小);
|
||||
* 同类型按召唤先后从该类型的一侧开始依次填充。
|
||||
*/
|
||||
public static readonly HERO_POSITIONS: Vec3[] = [
|
||||
v3(-220, BoxSet.GAME_LINE, 0), // index 0 (node_index 1): Top Front 第二
|
||||
v3(-140, BoxSet.GAME_LINE, 0), // index 1 (node_index 2): Mid Front 第一
|
||||
v3(-300, BoxSet.GAME_LINE, 0), // index 2 (node_index 3): Bot Front 第三
|
||||
// v3(-280, BoxSet.GAME_LINE + 100, 0), // index 3 (node_index 4): Top Back
|
||||
// v3(-280, BoxSet.GAME_LINE, 0), // index 4 (node_index 5): Mid Back
|
||||
// v3(-280, BoxSet.GAME_LINE - 100, 0), // index 5 (node_index 6): Bot Back
|
||||
v3(-320, BoxSet.GAME_LINE, 0),
|
||||
v3(-260, BoxSet.GAME_LINE, 0),
|
||||
v3(-200, BoxSet.GAME_LINE, 0),
|
||||
];
|
||||
|
||||
/** 英雄出生时的掉落高度(从空中落到地面的像素差) */
|
||||
@@ -102,34 +109,28 @@ export class MissionHeroComp extends CCComp {
|
||||
|
||||
// ======================== 事件处理 ========================
|
||||
|
||||
/** 关卡结束时清理全部英雄 ECS 实体 */
|
||||
/** 关卡结束时清理全部存活英雄 ECS 实体,并清空本局死亡英雄记录 */
|
||||
clear_heros() {
|
||||
const heroes = this.getAllHeroes();
|
||||
for (let i = 0; i < heroes.length; i++) {
|
||||
heroes[i].destroy();
|
||||
}
|
||||
// 整局结束,死亡记录失效(复活机制仅限局内)
|
||||
smc.mission.dead_heroes = [];
|
||||
}
|
||||
|
||||
/** 战斗准备阶段:重置出战英雄计数,恢复满血重新登场 */
|
||||
/**
|
||||
* 战斗准备阶段:刷新存活英雄计数与血条。
|
||||
*
|
||||
* 说明:英雄为真实死亡(实体已销毁,快照存于 smc.mission.dead_heroes),
|
||||
* 本阶段不再做回合自动复活;复活由后续复活机制(重新召唤)显式触发。
|
||||
*/
|
||||
fight_ready() {
|
||||
const heroes = this.getAllHeroes();
|
||||
smc.vmdata.mission_data.hero_num = heroes.length;
|
||||
for (let i = 0; i < heroes.length; i++) {
|
||||
const hero = heroes[i];
|
||||
const model = hero.get(HeroAttrsComp);
|
||||
const view = hero.get(HeroViewComp);
|
||||
if (model && view) {
|
||||
if (model.is_dead) {
|
||||
view.alive();
|
||||
const posIndex = this.pickPositionIndexForHero([hero.eid]);
|
||||
const landingPos = MissionHeroComp.HERO_POSITIONS[posIndex];
|
||||
// 计算出生点(空中)
|
||||
const spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
||||
view.node.setPosition(spawnPos);
|
||||
model.posIndex = posIndex;
|
||||
if (posIndex >= 0) smc.mission.heroGrid[posIndex] = hero.eid;
|
||||
hero.playDropAnim(spawnPos, landingPos.y);
|
||||
}
|
||||
const model = heroes[i].get(HeroAttrsComp);
|
||||
if (model) {
|
||||
model.dirty_hp = true;
|
||||
}
|
||||
}
|
||||
@@ -200,7 +201,7 @@ export class MissionHeroComp extends CCComp {
|
||||
* 动态分配英雄上场的位置
|
||||
* @param excludeEids 排除计算的实体ID数组(避免复活时把自己算成占据的位置)
|
||||
*/
|
||||
private pickPositionIndexForHero(excludeEids: number[] = []): number {
|
||||
private pickPositionIndexForHero(excludeEids: number[] = [], heroType: HType = HType.Melee): number {
|
||||
const heroes = this.getAllHeroes().filter(h => {
|
||||
const m = h.get(HeroAttrsComp);
|
||||
return m && !m.is_dead && !excludeEids.includes(h.eid);
|
||||
@@ -212,16 +213,18 @@ export class MissionHeroComp extends CCComp {
|
||||
if (m && m.posIndex >= 0) occupied.add(m.posIndex);
|
||||
}
|
||||
|
||||
// 优先中前(1) -> 上前(0) -> 下前(2) -> 中后(4) -> 上后(3) -> 下后(5)
|
||||
const slotPriority = [1, 0, 2, 4, 3, 5];
|
||||
// 近战优先靠前(x 更大,索引更大);远程/中程优先靠后(x 更小,索引更小)
|
||||
const slotPriority = heroType === HType.Melee
|
||||
? [2, 1, 0]
|
||||
: [0, 1, 2];
|
||||
for (const idx of slotPriority) {
|
||||
if (!occupied.has(idx)) {
|
||||
if (!occupied.has(idx) && MissionHeroComp.HERO_POSITIONS[idx]) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
// 溢出:默认中前
|
||||
return 1;
|
||||
// 溢出兜底:复用 index 0(坐标有效,避免越界崩溃)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,8 +255,9 @@ export class MissionHeroComp extends CCComp {
|
||||
private spawnHeroAt(uuid: number, hero_lv: number, card_lv: number, posIndex: number) {
|
||||
let hero = ecs.getEntity<Hero>(Hero);
|
||||
let scale = 1
|
||||
const finalPosIndex = posIndex >= 0 ? posIndex : this.pickPositionIndexForHero();
|
||||
const landingPos = MissionHeroComp.HERO_POSITIONS[finalPosIndex];
|
||||
const finalPosIndex = posIndex >= 0 ? posIndex : this.pickPositionIndexForHero([], HeroInfo[uuid]?.type ?? HType.Melee);
|
||||
// 兜底:索引越界或点位缺失时复用 index 0,保证 landingPos 一定有效
|
||||
const landingPos = MissionHeroComp.HERO_POSITIONS[finalPosIndex] ?? MissionHeroComp.HERO_POSITIONS[0];
|
||||
let spawnPos: Vec3 = v3(landingPos.x, landingPos.y + MissionHeroComp.HERO_DROP_HEIGHT, 0);
|
||||
hero.load(spawnPos, scale, uuid, landingPos.y, hero_lv, card_lv, finalPosIndex);
|
||||
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
/**
|
||||
* @file MissionMonComp.ts
|
||||
* @description 怪物(Monster)回合刷新管理组件(逻辑层)
|
||||
* @description 怪物(Monster)登场管理组件(逻辑层)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 管理每一回合怪物的生成计划:根据 RogueConfig 生成怪物。
|
||||
* 2. 自动推进回合:在准备阶段结束时(PhasePrepareEnd)启动分批释放。
|
||||
* 3. 分批刷怪:每回合固定 3 批,每 10 秒释放一批,批内按 MON_SPAWN_INTERVAL 逐个刷出。
|
||||
* 1. 预生成战斗怪物:FightReady 时通过 RogueSpawningEngine.generateBossCombo 随机一组 Boss 组合,并立即登场展示(供玩家侦查后再选英雄)。
|
||||
* 2. 战斗开始:PhasePrepareEnd 时恢复怪物行动(in_fight 前怪物仅原地待命,不会推进/索敌)。
|
||||
*
|
||||
* 关键设计:
|
||||
* - 所有怪物统一从右侧 X=400 出生点逐个刷出(MON_SPAWN_INTERVAL 节奏控制)。
|
||||
* - 实际阵型与推进由 MonMoveComp 在战斗中向左移动自然形成,不再使用固定网格点。
|
||||
* - 槽位索引(3行×4列)仅用于 monGrid 寻路与 SCastSystem 索敌定位。
|
||||
* - 上一回合残留怪在回合结束/开始时统一清理。
|
||||
* - 5 只怪在 X∈[100,320] 内按登场序号均匀铺开,Boss/小怪同时登场,玩家看清阵容后三选一。
|
||||
* - 实际推进由统一 MoveComp 在战斗中向敌人移动自然形成,不再使用固定网格点。
|
||||
* - 槽位索引仅用于 monGrid 寻路与 SCastSystem 索敌定位。
|
||||
*/
|
||||
import { _decorator, v3, Vec3 } from "cc";
|
||||
import { mLogger } from "../common/Logger";
|
||||
@@ -21,10 +19,10 @@ import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/O
|
||||
import { Monster } from "../hero/Mon";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { BoxSet, FacSet } from "../common/config/GameSet";
|
||||
import { spawningEngine, GeneratedMonster, TestModeConfig, MAX_MONSTERS, BATCH_COUNT, BATCH_INTERVAL, getWaveType, SPAWN_INTERVAL_BY_TYPE } from "./RogueConfig";
|
||||
import { BoxSet } from "../common/config/GameSet";
|
||||
import { spawningEngine, GeneratedMonster, TestModeConfig, BOSS_COUNT, MINION_COUNT } from "./RogueConfig";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { MonMoveComp } from "../hero/MonMoveComp";
|
||||
import { MoveComp } from "../hero/MoveComp";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -35,30 +33,25 @@ export class MissionMonCompComp extends CCComp {
|
||||
|
||||
/** 怪物出生掉落高度 */
|
||||
private static readonly MON_DROP_HEIGHT = 0;
|
||||
/** 怪物统一从右侧 X=400 出生点逐个刷出,随后向左推进 */
|
||||
private static readonly MON_SPAWN_X = 400;
|
||||
/** 逐个刷怪默认间隔(秒):保证怪物排成纵队,避免堆叠;运行时按回合类型查 SPAWN_INTERVAL_BY_TYPE 覆盖 */
|
||||
private static readonly MON_SPAWN_INTERVAL = 0.3;
|
||||
/** 怪物登场 X 范围下限(靠近英雄侧) */
|
||||
private static readonly SPAWN_X_MIN = 100;
|
||||
/** 怪物登场 X 范围上限(靠近右侧) */
|
||||
private static readonly SPAWN_X_MAX = 320;
|
||||
|
||||
/**
|
||||
* 怪物占位点:所有槽位统一以右侧出生点 (X=400) 作为坐标,
|
||||
* 实际阵型由 MonMoveComp 在战斗中向左推进时自然拉开。
|
||||
* 槽位索引仍保留 3 行 × 4 列结构,供 monGrid 寻路与 SCastSystem 索敌使用。
|
||||
* 超出 12 个槽位的怪物(放松回合)按 spawnIndex % 12 复用槽位。
|
||||
*/
|
||||
/** 槽位基准 Y(3行×4列结构仅供 monGrid 寻路与 SCastSystem 索敌定位) */
|
||||
public static readonly MON_POSITIONS: Vec3[] = [
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 0: Col1-Top
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 1: Col1-Mid
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 2: Col1-Bot
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 3: Col2-Top
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 4: Col2-Mid
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 5: Col2-Bot
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 6: Col3-Top
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 7: Col3-Mid
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 8: Col3-Bot
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 9: Col4-Top
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 10: Col4-Mid
|
||||
v3(MissionMonCompComp.MON_SPAWN_X, BoxSet.GAME_LINE, 0), // index 11: Col4-Bot
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 0: Col1-Top
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 1: Col1-Mid
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 2: Col1-Bot
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 3: Col2-Top
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 4: Col2-Mid
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 5: Col2-Bot
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 6: Col3-Top
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 7: Col3-Mid
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 8: Col3-Bot
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 9: Col4-Top
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 10: Col4-Mid
|
||||
v3(0, BoxSet.GAME_LINE, 0), // index 11: Col4-Bot
|
||||
];
|
||||
|
||||
// ======================== 编辑器属性 ========================
|
||||
@@ -70,114 +63,30 @@ export class MissionMonCompComp extends CCComp {
|
||||
|
||||
/** 全局生成顺序计数器(用于渲染层级排序) */
|
||||
private globalSpawnOrder: number = 0;
|
||||
/** 当前回合数 */
|
||||
private currentWave: number = 0;
|
||||
/** 当前回合的目标怪物总数 */
|
||||
/** 战斗场次(本局恒为 1,保留供 HUD/事件协议) */
|
||||
private currentWave: number = 1;
|
||||
/** 本场战斗的怪物总数 */
|
||||
private waveTargetCount: number = 0;
|
||||
/** 当前回合已生成的怪物数量 */
|
||||
/** 已登场的怪物数量 */
|
||||
private waveSpawnedCount: number = 0;
|
||||
/** 等待生成的怪物队列(按批次分组,batch 0~2) */
|
||||
private pendingBatches: GeneratedMonster[][] = [[], [], []];
|
||||
/** 当前正在释放的批次索引 */
|
||||
private currentBatch: number = 0;
|
||||
/** 批次释放计时器(秒) */
|
||||
private batchTimer: number = 0;
|
||||
/** 是否正在分批释放中 */
|
||||
private isReleasing: boolean = false;
|
||||
/** 逐个刷怪队列:从当前批次转入,按节奏在 update 中释放 */
|
||||
/** 待登场队列(FightReady 时整组进入,PhasePrepareEnd 时一次全量刷出) */
|
||||
private spawnQueue: GeneratedMonster[] = [];
|
||||
/** 逐个刷怪累计计时(秒) */
|
||||
private spawnTimer: number = 0;
|
||||
/** 当前回合的逐个刷怪间隔(秒),按回合类型查 SPAWN_INTERVAL_BY_TYPE */
|
||||
private spawnInterval: number = MissionMonCompComp.MON_SPAWN_INTERVAL;
|
||||
/** 当前批已刷出的怪物数(清场加速存活比例分母) */
|
||||
private batchReleasedCount: number = 0;
|
||||
/** 当前批是否已通过清场加速提前推进过(每批只触发一次) */
|
||||
private batchFastForwarded: boolean = false;
|
||||
/** 清场加速检测节流计时器(秒) */
|
||||
private aliveCheckTimer: number = 0;
|
||||
/** 本回合因清场加速累计节省的秒数(供 MissionComp 还原 DDA 判定口径,避免双重惩罚) */
|
||||
public waveEarlySkipTotal: number = 0;
|
||||
/** 清场加速触发阈值:当前批存活比例低于此值时提前推进(0.25 = 清掉 75%) */
|
||||
private static readonly BATCH_EARLY_RATIO = 0.25;
|
||||
/** 清场加速提前量(秒):等效 batchTimer 快进,保底批间隔不被瞬间叠爆 */
|
||||
private static readonly BATCH_EARLY_SKIP = 2.0;
|
||||
|
||||
// ======================== 生命周期 ========================
|
||||
|
||||
onLoad() {
|
||||
this.on(GameEvent.FightReady, this.fight_ready, this);
|
||||
this.on("PhasePrepareEnd", this.onPhasePrepareEnd, this);
|
||||
this.on("TimeUpAdvanceWave", this.onTimeUpAdvanceWave, this);
|
||||
}
|
||||
|
||||
protected update(dt: number): void {
|
||||
// 统计待刷出的怪物总数(未释放的批次 + 正在释放的队列)
|
||||
let pendingCount = this.spawnQueue.length;
|
||||
for (let i = this.currentBatch; i < BATCH_COUNT; i++) {
|
||||
pendingCount += this.pendingBatches[i].length;
|
||||
}
|
||||
smc.vmdata.mission_data.pending_mon_num = pendingCount;
|
||||
|
||||
// 场上怪物超阈值时暂停释放:冻结批次推进与逐个刷出计时(不推进计时器,恢复后从断点平滑续接)
|
||||
// 注意 pending 统计必须在 return 之前执行,保证回合结束检测(pending==0)语义不受暂停影响
|
||||
if (smc.mission.stop_spawn_mon) return;
|
||||
|
||||
// 分批释放:按 BATCH_INTERVAL 节奏推进批次
|
||||
if (this.isReleasing) {
|
||||
this.batchTimer += dt;
|
||||
if (this.batchTimer >= BATCH_INTERVAL && this.currentBatch < BATCH_COUNT - 1) {
|
||||
this.batchTimer = 0;
|
||||
this.advanceBatch();
|
||||
}
|
||||
|
||||
// 清场加速:0.2s 节流检测当前批存活比例,清得快则快进批次计时(学 PvZ 血量阈值提前刷新)
|
||||
this.aliveCheckTimer += dt;
|
||||
if (this.aliveCheckTimer >= 0.2) {
|
||||
this.aliveCheckTimer = 0;
|
||||
this.checkBatchEarlyAdvance();
|
||||
}
|
||||
}
|
||||
|
||||
// 逐个刷怪:按 spawnInterval 节奏从队列释放
|
||||
if (this.spawnQueue.length > 0) {
|
||||
this.spawnTimer += dt;
|
||||
if (this.spawnTimer >= this.spawnInterval) {
|
||||
this.spawnTimer = 0;
|
||||
const monData = this.spawnQueue.shift()!;
|
||||
const targetPosIndex = this.waveSpawnedCount % MissionMonCompComp.MON_POSITIONS.length;
|
||||
this.addMonsterAtGrid(targetPosIndex, monData, this.currentWave);
|
||||
this.waveSpawnedCount++;
|
||||
}
|
||||
}
|
||||
// 待登场统计(战斗结束检测与 HUD 进度消费)
|
||||
smc.vmdata.mission_data.pending_mon_num = this.spawnQueue.length;
|
||||
}
|
||||
|
||||
start() { }
|
||||
|
||||
private setupWaveData(monsters: GeneratedMonster[]) {
|
||||
// 按批次分组
|
||||
this.pendingBatches = [[], [], []];
|
||||
for (const m of monsters) {
|
||||
const batch = Math.min(m.batch, BATCH_COUNT - 1);
|
||||
this.pendingBatches[batch].push(m);
|
||||
}
|
||||
|
||||
this.waveTargetCount = monsters.length;
|
||||
smc.vmdata.mission_data.pending_mon_num = this.waveTargetCount;
|
||||
|
||||
let hasBoss = monsters.some(m => m.isBoss);
|
||||
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] 回合 ${this.currentWave} 生成怪物总数: ${this.waveTargetCount}`);
|
||||
|
||||
oops.message.dispatchEvent(GameEvent.NewWave, {
|
||||
wave: this.currentWave,
|
||||
total: this.waveTargetCount,
|
||||
bossWave: hasBoss,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 战斗准备:重置所有运行时状态并开始第一回合。
|
||||
* 战斗准备:重置运行时状态并预生成本场 Boss 组合。
|
||||
*/
|
||||
fight_ready() {
|
||||
smc.vmdata.mission_data.mon_num = 0;
|
||||
@@ -186,156 +95,40 @@ export class MissionMonCompComp extends CCComp {
|
||||
this.currentWave = 1;
|
||||
this.waveTargetCount = 0;
|
||||
this.waveSpawnedCount = 0;
|
||||
this.pendingBatches = [[], [], []];
|
||||
this.currentBatch = 0;
|
||||
this.batchTimer = 0;
|
||||
this.isReleasing = false;
|
||||
this.spawnQueue = [];
|
||||
this.spawnTimer = 0;
|
||||
this.batchReleasedCount = 0;
|
||||
this.batchFastForwarded = false;
|
||||
this.aliveCheckTimer = 0;
|
||||
this.waveEarlySkipTotal = 0;
|
||||
|
||||
// 预生成第一回合数据以获取数量和 Boss 信息
|
||||
const monsters = spawningEngine.generateWave(this.currentWave);
|
||||
this.setupWaveData(monsters);
|
||||
// 随机一组 Boss 组合(最终 Boss 战强度)
|
||||
const monsters = spawningEngine.generateBossCombo();
|
||||
this.spawnQueue = monsters;
|
||||
this.waveTargetCount = monsters.length;
|
||||
smc.vmdata.mission_data.pending_mon_num = this.waveTargetCount;
|
||||
|
||||
// 怪物先行登场:玩家侦查 Boss 组合后再做英雄三选一(in_fight 前怪物原地待命,不推进不索敌)
|
||||
const squad = this.spawnQueue;
|
||||
this.spawnQueue = [];
|
||||
smc.vmdata.mission_data.pending_mon_num = 0;
|
||||
for (const monData of squad) {
|
||||
this.addMonsterAtGrid(this.waveSpawnedCount, monData, this.currentWave);
|
||||
this.waveSpawnedCount++;
|
||||
}
|
||||
|
||||
if (TestModeConfig.enable) {
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] 测试模式已开启");
|
||||
}
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', `[MissionMonComp] Boss 组合生成完成,怪物总数: ${this.waveTargetCount}`);
|
||||
|
||||
mLogger.log(this.debugMode, 'MissionMonComp', "[MissionMonComp] Starting Wave System");
|
||||
}
|
||||
|
||||
// ======================== 回合管理 ========================
|
||||
|
||||
/**
|
||||
* 开始下一回合:回合数 +1 并预生成数据
|
||||
*/
|
||||
private onTimeUpAdvanceWave() {
|
||||
this.currentWave += 1;
|
||||
smc.vmdata.mission_data.level = this.currentWave;
|
||||
|
||||
const monsters = spawningEngine.generateWave(this.currentWave);
|
||||
this.setupWaveData(monsters);
|
||||
}
|
||||
|
||||
private onPhasePrepareEnd() {
|
||||
this.resetSlotSpawnData();
|
||||
|
||||
// 按回合类型确定本回合刷怪间隔(放松回合快速倾泻,压力回合稍慢聚焦)
|
||||
this.spawnInterval = SPAWN_INTERVAL_BY_TYPE[getWaveType(this.currentWave)] ?? MissionMonCompComp.MON_SPAWN_INTERVAL;
|
||||
|
||||
// 准备结束阶段:启动分批释放,
|
||||
// 第一批立即转入 spawnQueue,后续批次由 update 按 BATCH_INTERVAL 推进。
|
||||
this.startBatchRelease();
|
||||
}
|
||||
|
||||
// ======================== 分批释放 ========================
|
||||
|
||||
/** 启动分批释放:立即释放第一批,启动批次计时器 */
|
||||
private startBatchRelease() {
|
||||
this.currentBatch = 0;
|
||||
this.batchTimer = 0;
|
||||
this.isReleasing = true;
|
||||
this.releaseCurrentBatch();
|
||||
}
|
||||
|
||||
/** 推进到下一批次 */
|
||||
private advanceBatch() {
|
||||
if (this.currentBatch >= BATCH_COUNT - 1) {
|
||||
this.isReleasing = false;
|
||||
return;
|
||||
}
|
||||
this.currentBatch++;
|
||||
this.releaseCurrentBatch();
|
||||
}
|
||||
|
||||
/** 将当前批次的怪物转入 spawnQueue,等待逐个刷出 */
|
||||
private releaseCurrentBatch() {
|
||||
const batch = this.pendingBatches[this.currentBatch];
|
||||
if (batch.length === 0) {
|
||||
// 当前批次为空,尝试推进到下一批
|
||||
if (this.currentBatch < BATCH_COUNT - 1) {
|
||||
this.currentBatch++;
|
||||
this.releaseCurrentBatch();
|
||||
} else {
|
||||
this.isReleasing = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 将批次怪物转入逐个刷怪队列
|
||||
for (const m of batch) {
|
||||
this.spawnQueue.push(m);
|
||||
}
|
||||
this.batchReleasedCount = batch.length;
|
||||
this.batchFastForwarded = false;
|
||||
batch.length = 0;
|
||||
|
||||
// 让首个怪物在下一帧立即刷出,避免额外延迟
|
||||
this.spawnTimer = this.spawnInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清场加速检测:当前批已放完且存活比例 ≤ BATCH_EARLY_RATIO 时,快进批次计时器。
|
||||
* 只奖励"清得快"的 build(压缩批间垃圾时间),弱 build 清不完则不触发、不叠加压力。
|
||||
*/
|
||||
private checkBatchEarlyAdvance() {
|
||||
if (this.batchFastForwarded) return;
|
||||
if (this.currentBatch >= BATCH_COUNT - 1) return;
|
||||
if (this.spawnQueue.length > 0) return; // 本批还没放完,不判断
|
||||
if (this.batchReleasedCount <= 0) return;
|
||||
|
||||
let alive = 0;
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
|
||||
const a = e.get(HeroAttrsComp);
|
||||
if (a && a.fac === FacSet.MON && !a.is_dead) alive++;
|
||||
oops.message.dispatchEvent(GameEvent.NewWave, {
|
||||
wave: this.currentWave,
|
||||
total: this.waveTargetCount,
|
||||
bossWave: monsters.some(m => m.isBoss),
|
||||
});
|
||||
// alive 含前几批残留,比例 clamp 防低估
|
||||
const aliveRatio = Math.min(1, alive / this.batchReleasedCount);
|
||||
if (aliveRatio <= MissionMonCompComp.BATCH_EARLY_RATIO) {
|
||||
this.batchFastForwarded = true;
|
||||
this.batchTimer += MissionMonCompComp.BATCH_EARLY_SKIP;
|
||||
this.waveEarlySkipTotal += MissionMonCompComp.BATCH_EARLY_SKIP;
|
||||
smc.vmdata.mission_data.wave_early_skip = this.waveEarlySkipTotal;
|
||||
mLogger.log(this.debugMode, 'MissionMonComp',
|
||||
`[EarlyAdvance] batch=${this.currentBatch} alive=${alive}/${this.batchReleasedCount},快进 ${MissionMonCompComp.BATCH_EARLY_SKIP}s`);
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 槽位管理 ========================
|
||||
|
||||
/**
|
||||
* 清理上一回合残留怪物,并重置生成计数与逐个刷怪队列
|
||||
*/
|
||||
private resetSlotSpawnData() {
|
||||
ecs.query(ecs.allOf(HeroAttrsComp)).forEach(e => {
|
||||
const attrs = e.get(HeroAttrsComp);
|
||||
if (attrs && attrs.fac === FacSet.MON && !attrs.is_dead) {
|
||||
e.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
this.waveSpawnedCount = 0;
|
||||
// 同步丢弃上一回合未释放的队列,避免与新一回合混合
|
||||
this.spawnQueue = [];
|
||||
this.spawnTimer = 0;
|
||||
this.isReleasing = false;
|
||||
this.currentBatch = 0;
|
||||
this.batchTimer = 0;
|
||||
this.batchReleasedCount = 0;
|
||||
this.batchFastForwarded = false;
|
||||
this.aliveCheckTimer = 0;
|
||||
this.waveEarlySkipTotal = 0;
|
||||
smc.vmdata.mission_data.wave_early_skip = 0;
|
||||
}
|
||||
|
||||
// ======================== 怪物生成 ========================
|
||||
|
||||
/**
|
||||
* 在指定位置索引处生成一个怪物
|
||||
* 5 只怪在 X∈[100,320] 内按登场序号均匀铺开,Boss/小怪同时登场直接开打
|
||||
*/
|
||||
private addMonsterAtGrid(
|
||||
posIndex: number,
|
||||
@@ -346,8 +139,10 @@ export class MissionMonCompComp extends CCComp {
|
||||
let scale = -1;
|
||||
|
||||
const basePos = MissionMonCompComp.MON_POSITIONS[posIndex % MissionMonCompComp.MON_POSITIONS.length];
|
||||
const spawnX = basePos.x;
|
||||
const landingY = basePos.y + (monData.isBoss ? 6 : 0);
|
||||
const spawnX = MissionMonCompComp.SPAWN_X_MIN
|
||||
+ (MissionMonCompComp.SPAWN_X_MAX - MissionMonCompComp.SPAWN_X_MIN)
|
||||
* (posIndex / Math.max(1, BOSS_COUNT + MINION_COUNT - 1));
|
||||
const landingY = basePos.y;
|
||||
const spawnPos: Vec3 = v3(spawnX, landingY + MissionMonCompComp.MON_DROP_HEIGHT, 0);
|
||||
this.globalSpawnOrder = (this.globalSpawnOrder + 1) % 999;
|
||||
|
||||
@@ -363,12 +158,12 @@ export class MissionMonCompComp extends CCComp {
|
||||
oops.message.dispatchEvent(GameEvent.BossSpawn, { pos: spawnPos.clone() });
|
||||
}
|
||||
|
||||
const move = mon.get(MonMoveComp);
|
||||
const move = mon.get(MoveComp);
|
||||
if (move) {
|
||||
move.spawnOrder = this.globalSpawnOrder;
|
||||
}
|
||||
|
||||
// 应用新引擎计算好的最终属性
|
||||
// 应用引擎计算好的最终属性
|
||||
const model = mon.get(HeroAttrsComp);
|
||||
if (model) {
|
||||
model.ap = monData.ap;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,6 @@
|
||||
* - Interval (2):监听 FightStart → update 帧驱动按 t_inv 间隔重复触发(按 t_times 控制每回合次数)
|
||||
* - Field (3):被动生效,不主动施法(实际由 FieldSkillSet 处理)
|
||||
* - FightStart (4):监听 FightStart 事件,按 trigger_limit 全局累计上限
|
||||
* - FightEnd (5):监听 FightEnd 事件(每回合结束派发),按 trigger_limit 全局累计上限
|
||||
* - HeroDead (6):监听 HeroDead 事件(仅英雄阵营派发,怪物死亡不触发)
|
||||
* - HeroCall (7):监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
@@ -145,7 +144,6 @@ export class SkillBoxComp extends CCComp {
|
||||
oops.message.off(GameEvent.MissionEnd, this.onMissionEnd, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onFightStart, this);
|
||||
oops.message.off(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.MasterCalled, this.onEventTrigger, this);
|
||||
oops.message.off(GameEvent.ReviveSuccess, this.onEventTrigger, this);
|
||||
@@ -289,7 +287,6 @@ export class SkillBoxComp extends CCComp {
|
||||
* - Interval: 监听 FightStart,进入战斗后由 update 帧驱动计时
|
||||
* - Field: 不主动施法(实际生效由 FieldSkillSet 处理)
|
||||
* - FightStart: 监听 FightStart 事件
|
||||
* - FightEnd: 监听 FightEnd 事件
|
||||
* - HeroDead: 监听 HeroDead 事件(已在派发处做阵营过滤)
|
||||
* - HeroCall: 监听 MasterCalled(主角/技能召唤)+ ReviveSuccess(复活)
|
||||
*
|
||||
@@ -320,10 +317,6 @@ export class SkillBoxComp extends CCComp {
|
||||
oops.message.on(GameEvent.FightStart, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.FightEnd:
|
||||
oops.message.on(GameEvent.FightEnd, this.onEventTrigger, this);
|
||||
break;
|
||||
|
||||
case CardTriggerType.HeroDead:
|
||||
oops.message.on(GameEvent.HeroDead, this.onEventTrigger, this);
|
||||
break;
|
||||
@@ -345,7 +338,7 @@ export class SkillBoxComp extends CCComp {
|
||||
* 事件型触发的统一入口:
|
||||
* - Instant 类型:按 trigger_times 上限判定,复用 current_trigger_times 跟踪
|
||||
* (保持与原 is_instant 行为一致,且 NewWave 中也用 current_trigger_times)
|
||||
* - 事件型(FightStart/FightEnd/HeroDead/HeroCall):按 trigger_limit 上限判定,使用 trigger_count 跟踪
|
||||
* - 事件型(FightStart/HeroDead/HeroCall):按 trigger_limit 上限判定,使用 trigger_count 跟踪
|
||||
*
|
||||
* 注意:本方法不读取事件 payload,仅作触发信号使用(避免 MasterCalled 不同 payload 字段引发的兼容问题)。
|
||||
*/
|
||||
@@ -441,7 +434,6 @@ export class SkillBoxComp extends CCComp {
|
||||
const showRemainCount =
|
||||
this.trigger_type === CardTriggerType.Interval ||
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
|
||||
@@ -450,7 +442,6 @@ export class SkillBoxComp extends CCComp {
|
||||
// 事件型按 trigger_limit;Interval 按 t_times
|
||||
const isEvent =
|
||||
this.trigger_type === CardTriggerType.FightStart ||
|
||||
this.trigger_type === CardTriggerType.FightEnd ||
|
||||
this.trigger_type === CardTriggerType.HeroDead ||
|
||||
this.trigger_type === CardTriggerType.HeroCall;
|
||||
const used = isEvent ? this.trigger_count : this.current_trigger_times;
|
||||
@@ -520,7 +511,7 @@ export class SkillBoxComp extends CCComp {
|
||||
* 处理维持回合逻辑:递减剩余回合,或者重置触发次数。
|
||||
*
|
||||
* 各类型在新一回合的行为:
|
||||
* - Instant/Interval/FightStart/FightEnd:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
|
||||
* - Instant/Interval/FightStart:按 keep_waves 决定维持/销毁,并在新一回合开始时重置本地计数
|
||||
* - Field:被动生效,跟随 keep_waves 决定存活
|
||||
* - HeroDead/HeroCall:跨回合触发的事件型,trigger_count(全局)不重置,仅 keep_waves 控制存活
|
||||
*/
|
||||
|
||||
@@ -1,101 +1,74 @@
|
||||
/**
|
||||
* @file WaveHudComp.ts
|
||||
* @description 波次进度 HUD 组件(表现层)
|
||||
* @description 战斗进度 HUD 组件(表现层)
|
||||
*
|
||||
* 职责:
|
||||
* 1. 本回合清剿进度:剩余怪数(mon_num + pending_mon_num)+ 进度条。
|
||||
* 2. 全局 20 回合进度格:静态生成,Boss 回合(wave%5==0)置旗帜样式,当前回合脉动、已过回合置灰。
|
||||
* 1. 清剿进度:剩余怪数(mon_num + pending_mon_num)+ 进度条。
|
||||
* 2. Boss 战旗帜:单场 Boss 战定胜负,仅 1 格旗帜,战斗中脉动。
|
||||
*
|
||||
* 数据流:NewWave 事件缓存 total/wave;update 0.2s 降频轮询 vmdata(与 syncMonsterSpawnState 同节奏)。
|
||||
* 使用 oops-framework 模块:oops.message(事件解耦)。
|
||||
*
|
||||
* 编辑器绑定:waveLab / remainLab / progress / flagsRoot(水平 Layout 容器,20 格代码生成)。
|
||||
* 编辑器绑定:waveLab / remainLab / progress / flagsRoot(水平 Layout 容器,1 格代码生成)。
|
||||
*/
|
||||
import { _decorator, Node, Label, ProgressBar, Sprite, Color, tween, Tween, v3, UITransform } from "cc";
|
||||
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { getWaveType, WaveType, MAX_WAVE } from "./RogueConfig";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('WaveHudComp')
|
||||
export class WaveHudComp extends CCComp {
|
||||
@property({ type: Label, tooltip: "回合文本(第 x/20 回合)" })
|
||||
@property({ type: Label, tooltip: "战斗标题文本" })
|
||||
waveLab: Label | null = null;
|
||||
|
||||
@property({ type: Label, tooltip: "剩余怪数文本" })
|
||||
remainLab: Label | null = null;
|
||||
|
||||
@property({ type: ProgressBar, tooltip: "本回合清剿进度条" })
|
||||
@property({ type: ProgressBar, tooltip: "清剿进度条" })
|
||||
progress: ProgressBar | null = null;
|
||||
|
||||
@property({ type: Node, tooltip: "全局回合旗帜容器(水平 Layout,20 格代码生成)" })
|
||||
@property({ type: Node, tooltip: "Boss 战旗帜容器(水平 Layout,1 格代码生成)" })
|
||||
flagsRoot: Node | null = null;
|
||||
|
||||
/** 本回合怪物总数(NewWave 事件载荷缓存) */
|
||||
/** 本场怪物总数(NewWave 事件载荷缓存) */
|
||||
private waveTotal: number = 0;
|
||||
/** HUD 刷新节流计时器 */
|
||||
private hudTimer: number = 0;
|
||||
/** 20 格旗帜节点缓存(Boss 位为旗帜样式) */
|
||||
private flagNodes: Node[] = [];
|
||||
/** Boss 战旗帜节点 */
|
||||
private flagNode: Node | null = null;
|
||||
|
||||
onLoad() {
|
||||
// oops.message 全局事件总线:新回合
|
||||
// oops.message 全局事件总线:新战斗
|
||||
oops.message.on(GameEvent.NewWave, this.onNewWave, this);
|
||||
this.buildFlags();
|
||||
this.buildFlag();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
oops.message.off(GameEvent.NewWave, this.onNewWave, this);
|
||||
}
|
||||
|
||||
/** 静态生成 20 格回合进度(Boss 位按 getWaveType 静态预知,无需运行数据) */
|
||||
private buildFlags() {
|
||||
/** 静态生成 1 格 Boss 战旗帜 */
|
||||
private buildFlag() {
|
||||
if (!this.flagsRoot) return;
|
||||
for (let i = 1; i <= MAX_WAVE; i++) {
|
||||
const cell = new Node(`flag_${i}`);
|
||||
cell.addComponent(UITransform).setContentSize(14, 14);
|
||||
const sp = cell.addComponent(Sprite);
|
||||
// Boss 位用警示色方块,普通位用圆点色(无美术资源时以颜色区分,后续可换 spriteFrame)
|
||||
const isBoss = getWaveType(i) === WaveType.Pressure;
|
||||
sp.color = isBoss ? new Color(220, 50, 50) : new Color(120, 120, 120);
|
||||
sp.sizeMode = Sprite.SizeMode.CUSTOM;
|
||||
cell.parent = this.flagsRoot;
|
||||
this.flagNodes.push(cell);
|
||||
}
|
||||
const cell = new Node("flag_boss");
|
||||
cell.addComponent(UITransform).setContentSize(14, 14);
|
||||
const sp = cell.addComponent(Sprite);
|
||||
sp.color = new Color(220, 50, 50);
|
||||
sp.sizeMode = Sprite.SizeMode.CUSTOM;
|
||||
cell.parent = this.flagsRoot;
|
||||
this.flagNode = cell;
|
||||
}
|
||||
|
||||
private onNewWave(event: string, data: { wave: number; total: number; bossWave: boolean }) {
|
||||
this.waveTotal = data.total;
|
||||
if (this.waveLab) this.waveLab.string = `第 ${data.wave}/${MAX_WAVE} 回合`;
|
||||
this.refreshFlags(data.wave);
|
||||
if (this.waveLab) this.waveLab.string = "最终 Boss 战";
|
||||
if (this.flagNode) this.playFlagPulse(this.flagNode);
|
||||
}
|
||||
|
||||
/** 刷新旗帜状态:已过回合置暗,当前回合 Boss 旗脉动 */
|
||||
private refreshFlags(currentWave: number) {
|
||||
for (let i = 0; i < this.flagNodes.length; i++) {
|
||||
const cell = this.flagNodes[i];
|
||||
const wave = i + 1;
|
||||
const sp = cell.getComponent(Sprite)!;
|
||||
const isBoss = getWaveType(wave) === WaveType.Pressure;
|
||||
if (wave < currentWave) {
|
||||
sp.color = new Color(70, 70, 70); // 已过回合置暗
|
||||
cell.setScale(v3(1, 1, 1));
|
||||
Tween.stopAllByTarget(cell);
|
||||
} else if (wave === currentWave) {
|
||||
sp.color = isBoss ? new Color(255, 80, 80) : new Color(255, 220, 100);
|
||||
if (isBoss) this.playFlagPulse(cell);
|
||||
} else {
|
||||
sp.color = isBoss ? new Color(220, 50, 50) : new Color(120, 120, 120);
|
||||
cell.setScale(v3(1, 1, 1));
|
||||
Tween.stopAllByTarget(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 当前回合 Boss 旗缩放脉动 */
|
||||
/** Boss 战旗帜缩放脉动 */
|
||||
private playFlagPulse(cell: Node) {
|
||||
Tween.stopAllByTarget(cell);
|
||||
tween(cell)
|
||||
|
||||
@@ -74,6 +74,9 @@ export class SMoveDataComp extends ecs.Comp {
|
||||
|
||||
/** 是否自动销毁(到达目标后) */
|
||||
autoDestroy: boolean = true;
|
||||
|
||||
/** 直线飞行距离(仅 runType=linear 生效,<=0 时延长终点飞出屏幕) */
|
||||
distance: number = 0;
|
||||
|
||||
reset() {
|
||||
this.startPos.set(0, 0, 0);
|
||||
@@ -98,6 +101,7 @@ export class SMoveDataComp extends ecs.Comp {
|
||||
this.startTime = 0;
|
||||
this.totalTime = 0;
|
||||
this.autoDestroy = true;
|
||||
this.distance = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { v3, Vec3 ,Node} from "cc";
|
||||
import { v3, Vec3, Node } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { EType, RType, SkillSet } from "../common/config/SkillSet";
|
||||
import { SMoveDataComp } from "./SMoveComp";
|
||||
@@ -26,46 +26,46 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
filter(): ecs.IMatcher {
|
||||
return ecs.allOf(SMoveDataComp, SkillView);
|
||||
}
|
||||
|
||||
|
||||
entityEnter(entity: ecs.Entity): void {
|
||||
const moveComp = entity.get(SMoveDataComp);
|
||||
const skillView = entity.get(SkillView);
|
||||
|
||||
|
||||
if (!moveComp || !skillView || !skillView.node) return;
|
||||
|
||||
|
||||
// 获取技能配置
|
||||
const skillConfig = SkillSet[moveComp.s_uuid];
|
||||
if (!skillConfig) {
|
||||
mLogger.warn(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能配置不存在: ${moveComp.s_uuid}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据配置设置移动速度与加速度
|
||||
if (skillConfig.speed > 0) {
|
||||
|
||||
// 移动参数由 prefab 的 SkillView @property 决定(speed<=0 时 Skill.load 已回退配置表值)
|
||||
if (moveComp.speed <= 0) {
|
||||
moveComp.speed = skillConfig.speed;
|
||||
}
|
||||
if (skillConfig.is_accel) {
|
||||
moveComp.isAccelerate = true;
|
||||
}
|
||||
|
||||
|
||||
// 根据runType设置初始位置
|
||||
this.initializePosition(moveComp, skillView);
|
||||
|
||||
|
||||
// 开始移动(除了固定位置类型)
|
||||
if (moveComp.runType !== RType.fixed && moveComp.runType !== RType.fixedEnd) {
|
||||
moveComp.startMove();
|
||||
}
|
||||
|
||||
|
||||
mLogger.log(this.debugMode, 'SMoveSystem', `[SMoveSystem] 技能 ${skillConfig.name} 开始移动,类型: ${moveComp.runType}`);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据runType初始化技能位置
|
||||
*/
|
||||
private initializePosition(moveComp: SMoveDataComp, skillView: SkillView): void {
|
||||
const node = skillView.node;
|
||||
|
||||
switch(moveComp.runType) {
|
||||
|
||||
switch (moveComp.runType) {
|
||||
case RType.linear:
|
||||
// linear类型:根据atk_x和atk_y调整起始位置
|
||||
const adjustedStartPos = v3(
|
||||
@@ -73,35 +73,50 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
moveComp.startPos.y + moveComp.atk_y,
|
||||
moveComp.startPos.z
|
||||
);
|
||||
|
||||
|
||||
const originTargetPos = v3(
|
||||
moveComp.targetPos.x,
|
||||
moveComp.targetPos.y,
|
||||
moveComp.targetPos.z
|
||||
);
|
||||
|
||||
|
||||
const direction = new Vec3();
|
||||
Vec3.subtract(direction, originTargetPos, adjustedStartPos);
|
||||
|
||||
// 延长终止点,统一消亡点的x坐标为 +-500
|
||||
const targetX = moveComp.scale > 0 ? 500 : -500;
|
||||
let targetY = originTargetPos.y;
|
||||
|
||||
if (Math.abs(direction.x) > 0.01) {
|
||||
const slope = direction.y / direction.x;
|
||||
targetY = adjustedStartPos.y + slope * (targetX - adjustedStartPos.x);
|
||||
|
||||
let adjustedTargetPos: Vec3;
|
||||
if (moveComp.distance > 0) {
|
||||
// 距离模式:沿起始指向目标的方向飞行固定距离
|
||||
if (direction.length() < 0.01) {
|
||||
// 起终点重合时退化为按朝向水平飞行
|
||||
direction.set(moveComp.scale > 0 ? 1 : -1, 0, 0);
|
||||
}
|
||||
direction.normalize();
|
||||
adjustedTargetPos = v3(
|
||||
adjustedStartPos.x + direction.x * moveComp.distance,
|
||||
adjustedStartPos.y + direction.y * moveComp.distance,
|
||||
originTargetPos.z
|
||||
);
|
||||
} else {
|
||||
// 延长终止点,统一消亡点的x坐标为 +-500
|
||||
const targetX = moveComp.scale > 0 ? 500 : -500;
|
||||
let targetY = originTargetPos.y;
|
||||
|
||||
if (Math.abs(direction.x) > 0.01) {
|
||||
const slope = direction.y / direction.x;
|
||||
targetY = adjustedStartPos.y + slope * (targetX - adjustedStartPos.x);
|
||||
}
|
||||
|
||||
adjustedTargetPos = v3(
|
||||
targetX,
|
||||
targetY,
|
||||
originTargetPos.z
|
||||
);
|
||||
}
|
||||
|
||||
const adjustedTargetPos = v3(
|
||||
targetX,
|
||||
targetY,
|
||||
originTargetPos.z
|
||||
);
|
||||
|
||||
|
||||
moveComp.startPos.set(adjustedStartPos);
|
||||
moveComp.targetPos.set(adjustedTargetPos);
|
||||
node.setPosition(adjustedStartPos);
|
||||
|
||||
|
||||
// 设置旋转角度
|
||||
const dirForAngle = new Vec3();
|
||||
Vec3.subtract(dirForAngle, adjustedTargetPos, adjustedStartPos);
|
||||
@@ -113,12 +128,12 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
node.angle = angle;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case RType.fixed:
|
||||
// 固定起始位置
|
||||
node.setPosition(moveComp.startPos.x, node.position.y, 0);
|
||||
break;
|
||||
|
||||
|
||||
case RType.fixedEnd:
|
||||
// 固定结束位置
|
||||
node.setPosition(moveComp.targetPos.x > 360 ? 300 : moveComp.targetPos.x, moveComp.targetPos.y, 0);
|
||||
@@ -154,26 +169,26 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
|
||||
update(entity: ecs.Entity): void {
|
||||
if(!smc.mission.play ) return;
|
||||
if(smc.mission.pause) return
|
||||
if (!smc.mission.play) return;
|
||||
if (smc.mission.pause) return
|
||||
const moveComp = entity.get(SMoveDataComp);
|
||||
const skillView = entity.get(SkillView);
|
||||
|
||||
|
||||
if (!moveComp || !skillView || !skillView.node) return;
|
||||
|
||||
|
||||
// 更新移动进度
|
||||
const isMoving = moveComp.updateProgress(this.dt);
|
||||
|
||||
|
||||
if (isMoving) {
|
||||
// 更新节点位置
|
||||
skillView.node.setPosition(moveComp.currentPos);
|
||||
|
||||
|
||||
// 处理自动旋转(贝塞尔移动)
|
||||
if (moveComp.runType === RType.bezier) {
|
||||
this.updateRotation(skillView.node, moveComp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查移动完成
|
||||
if (moveComp.isCompleted && moveComp.autoDestroy) {
|
||||
// 根据结束类型决定是否销毁
|
||||
@@ -185,7 +200,7 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新节点旋转(用于贝塞尔移动)
|
||||
*/
|
||||
@@ -193,29 +208,29 @@ export class SMoveSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
if (moveComp.progress < 1) {
|
||||
// 计算下一帧的位置来确定方向
|
||||
const nextProgress = Math.min(moveComp.progress + 0.01, 1);
|
||||
const t = moveComp.isAccelerate ?
|
||||
(nextProgress * nextProgress * 0.7 + nextProgress * 0.3) :
|
||||
const t = moveComp.isAccelerate ?
|
||||
(nextProgress * nextProgress * 0.7 + nextProgress * 0.3) :
|
||||
nextProgress;
|
||||
const nextPos = v3(0, 0, 0);
|
||||
|
||||
|
||||
// 计算下一个位置
|
||||
const oneMinusT = 1 - t;
|
||||
const oneMinusTSquared = oneMinusT * oneMinusT;
|
||||
const tSquared = t * t;
|
||||
const twoOneMinusTt = 2 * oneMinusT * t;
|
||||
|
||||
nextPos.x = oneMinusTSquared * moveComp.startPos.x +
|
||||
twoOneMinusTt * moveComp.controlPoint.x +
|
||||
tSquared * moveComp.targetPos.x;
|
||||
|
||||
nextPos.y = oneMinusTSquared * moveComp.startPos.y +
|
||||
twoOneMinusTt * moveComp.controlPoint.y +
|
||||
tSquared * moveComp.targetPos.y;
|
||||
|
||||
|
||||
nextPos.x = oneMinusTSquared * moveComp.startPos.x +
|
||||
twoOneMinusTt * moveComp.controlPoint.x +
|
||||
tSquared * moveComp.targetPos.x;
|
||||
|
||||
nextPos.y = oneMinusTSquared * moveComp.startPos.y +
|
||||
twoOneMinusTt * moveComp.controlPoint.y +
|
||||
tSquared * moveComp.targetPos.y;
|
||||
|
||||
// 计算方向角度
|
||||
const direction = new Vec3();
|
||||
Vec3.subtract(direction, nextPos, moveComp.currentPos);
|
||||
|
||||
|
||||
if (direction.length() > 0.01) {
|
||||
let angle = Math.atan2(direction.y, direction.x) * (180 / Math.PI);
|
||||
if (moveComp.scale < 0) {
|
||||
@@ -236,15 +251,15 @@ export class SMoveHelper {
|
||||
*/
|
||||
static setupMoveForSkill(entity: ecs.Entity, startPos: Vec3, targetPos: Vec3, s_uuid: number): void {
|
||||
let moveComp = entity.get(SMoveDataComp);
|
||||
|
||||
|
||||
if (!moveComp) {
|
||||
moveComp = entity.add(SMoveDataComp);
|
||||
}
|
||||
|
||||
|
||||
moveComp.startPos.set(startPos);
|
||||
moveComp.targetPos.set(targetPos);
|
||||
moveComp.s_uuid = s_uuid;
|
||||
|
||||
|
||||
// 从技能配置获取默认参数
|
||||
const skillConfig = SkillSet[s_uuid];
|
||||
if (skillConfig) {
|
||||
@@ -253,7 +268,7 @@ export class SMoveHelper {
|
||||
if (skillConfig.is_accel) moveComp.isAccelerate = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查技能是否正在移动
|
||||
*/
|
||||
@@ -261,7 +276,7 @@ export class SMoveHelper {
|
||||
const moveComp = entity.get(SMoveDataComp);
|
||||
return moveComp ? moveComp.isMoving : false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取技能移动统计信息
|
||||
*/
|
||||
@@ -269,7 +284,7 @@ export class SMoveHelper {
|
||||
const moveComp = entity.get(SMoveDataComp);
|
||||
return moveComp ? moveComp.getMoveStats() : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 停止技能移动
|
||||
*/
|
||||
|
||||
@@ -146,7 +146,7 @@ export class Skill extends ecs.Entity {
|
||||
if (node.isValid) node.destroy();
|
||||
return;
|
||||
}
|
||||
if (config.EType != EType.collision) {
|
||||
if (SView.endType != EType.collision) {
|
||||
const collider = node.getComponent(BoxCollider2D);
|
||||
if (collider) {
|
||||
collider.enabled = false
|
||||
@@ -174,21 +174,24 @@ export class Skill extends ecs.Entity {
|
||||
sMoveCom.targetPos.set(targetPos);
|
||||
sMoveCom.s_uuid = s_uuid;
|
||||
sMoveCom.scale = caster.node.scale.x < 0 ? -1 : 1;
|
||||
sMoveCom.runType = config.RType;
|
||||
sMoveCom.endType = config.EType;
|
||||
// runType/endType/speed/time 由 prefab 的 SkillView @property 决定,配置表同名字段仅作兜底
|
||||
sMoveCom.runType = SView.runType;
|
||||
sMoveCom.endType = SView.endType;
|
||||
sMoveCom.speed = SView.speed > 0 ? SView.speed : config.speed;
|
||||
sMoveCom.bezierStartHeight = config.bezier_start_y ?? sMoveCom.bezierStartHeight;
|
||||
sMoveCom.bezierMidHeight = config.bezier_mid_y ?? sMoveCom.bezierMidHeight;
|
||||
sMoveCom.bezierArc = config.bezier_arc ?? sMoveCom.bezierArc;
|
||||
// 从SkillView获取移动参数,位置初始化由SMoveSystem统一处理
|
||||
sMoveCom.atk_x = SView.atk_x;
|
||||
sMoveCom.atk_y = SView.atk_y;
|
||||
sMoveCom.distance = SView.distance;
|
||||
|
||||
if (config.EType === EType.timeEnd) {
|
||||
if (SView.endType === EType.timeEnd) {
|
||||
let sTimeCom = this.get(StimeDataComp);
|
||||
if (!sTimeCom) sTimeCom = this.add(StimeDataComp);
|
||||
sTimeCom.reset();
|
||||
sTimeCom.s_uuid = s_uuid;
|
||||
sTimeCom.totalTime = Math.max(1, config.time ?? 0);
|
||||
sTimeCom.totalTime = Math.max(1, SView.time > 0 ? SView.time : (config.time ?? 0));
|
||||
sTimeCom.hitInterval = Math.max(0.5, config.hitcd || 0);
|
||||
} else {
|
||||
const sTimeCom = this.get(StimeDataComp);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { _decorator, Animation, CCInteger, Collider2D, Contact2DType, UITransform, v3, Vec3 } from "cc";
|
||||
import { _decorator, Animation, CCFloat, CCInteger, Collider2D, Contact2DType, Enum, UITransform, v3, Vec3 } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { HeroViewComp } from "../hero/HeroViewComp";
|
||||
import { DTType, EType, SkillConfig, SkillSet } from "../common/config/SkillSet";
|
||||
import { DTType, EType, RType, SkillConfig, SkillSet } from "../common/config/SkillSet";
|
||||
import { SDataCom } from "./SDataCom";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { DamageQueueHelper } from "../hero/DamageQueueComp";
|
||||
@@ -22,13 +22,33 @@ export class SkillView extends CCComp {
|
||||
@property({ type: CCInteger })
|
||||
atk_y: number = 0
|
||||
|
||||
/** 运动类型:直线/贝塞尔/固定起点/固定终点(由 prefab 配置,优先级高于 SkillSet) */
|
||||
@property({ type: Enum(RType), tooltip: "技能特效运动类型" })
|
||||
runType: RType = RType.linear;
|
||||
|
||||
/** 消亡条件:动画结束/计时结束/碰撞次数结束(由 prefab 配置,优先级高于 SkillSet) */
|
||||
@property({ type: Enum(EType), tooltip: "技能特效消亡条件" })
|
||||
endType: EType = EType.collision;
|
||||
|
||||
/** 移动速度(由 prefab 配置,<=0 时回退 SkillSet.speed) */
|
||||
@property({ type: CCFloat, slide: true, range: [0, 2000, 10], tooltip: "技能特效移动速度" })
|
||||
speed: number = 0;
|
||||
|
||||
/** timeEnd 持续时间(秒),仅 endType=timeEnd 时生效(由 prefab 配置,<=0 时回退 SkillSet.time) */
|
||||
@property({ type: CCFloat, slide: true, range: [0, 30, 0.1], tooltip: "timeEnd 持续时间(秒)" })
|
||||
time: number = 0;
|
||||
|
||||
/** 直线飞行距离(像素),仅 runType=linear 时生效(由 prefab 配置,<=0 时保持默认飞出屏幕的行为) */
|
||||
@property({ type: CCFloat, slide: true, range: [0, 2000, 10], tooltip: "直线飞行距离(仅 linear 生效)" })
|
||||
distance: number = 0;
|
||||
|
||||
private debugMode: boolean = false;
|
||||
|
||||
anim:Animation=null;
|
||||
group:number=0;
|
||||
SConf:SkillConfig=null;
|
||||
sData:SDataCom=null;
|
||||
s_uuid:number=1001
|
||||
anim: Animation = null;
|
||||
group: number = 0;
|
||||
SConf: SkillConfig = null;
|
||||
sData: SDataCom = null;
|
||||
s_uuid: number = 1001
|
||||
private collider: Collider2D = null; // 缓存碰撞体引用
|
||||
private pendingDisableCollider: boolean = false;
|
||||
private isDisposing: boolean = false;
|
||||
@@ -41,25 +61,25 @@ export class SkillView extends CCComp {
|
||||
this.pendingDisableCollider = false;
|
||||
this.isDisposing = false;
|
||||
this.collider = this.getComponent(Collider2D);
|
||||
if(this.collider) {
|
||||
if (this.collider) {
|
||||
this.collider.group = this.group;
|
||||
this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
|
||||
this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this);
|
||||
this.collider.enabled = this.SConf?.EType === EType.collision;
|
||||
this.collider.enabled = this.endType === EType.collision;
|
||||
}
|
||||
if(this.node.getComponent(Animation)){
|
||||
if (this.node.getComponent(Animation)) {
|
||||
let anim = this.node.getComponent(Animation);
|
||||
mLogger.log(this.debugMode, 'SkillView', "[SkillCom]:has anim",anim)
|
||||
mLogger.log(this.debugMode, 'SkillView', "[SkillCom]:has anim", anim)
|
||||
anim.off(Animation.EventType.FINISHED, this.onAnimationFinished, this);
|
||||
anim.on(Animation.EventType.FINISHED, this.onAnimationFinished, this);
|
||||
|
||||
|
||||
// 对象池复用时,需要手动播放默认动画(因为 Play On Load 只在首次生效)
|
||||
if (anim.defaultClip) {
|
||||
anim.play(anim.defaultClip.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
onBeginContact (seCol: Collider2D, oCol: Collider2D) {
|
||||
onBeginContact(seCol: Collider2D, oCol: Collider2D) {
|
||||
if (!this.sData || !this.SConf) {
|
||||
mLogger.warn(this.debugMode, 'SkillView', '[SkillView] onBeginContact 缺少 sData 或 SConf,忽略此次碰撞');
|
||||
return;
|
||||
@@ -126,17 +146,23 @@ export class SkillView extends CCComp {
|
||||
this.apply_damage(targetView, this.sData.hit_count)
|
||||
}
|
||||
|
||||
onAnimationFinished(){
|
||||
// animationEnd 类型:动画结束后再关闭碰撞并销毁
|
||||
if(this.SConf.EType==EType.animationEnd){
|
||||
onAnimationFinished() {
|
||||
// animationEnd 类型:动画结束后再关闭碰撞并销毁
|
||||
if (this.endType == EType.animationEnd) {
|
||||
this.disable_collider_now();
|
||||
this.ent.destroy()
|
||||
}
|
||||
return;
|
||||
}
|
||||
// fixed 类型不移动,无法通过移动完成销毁;动画播完作为兜底销毁时机
|
||||
if (this.runType == RType.fixed) {
|
||||
this.disable_collider_now();
|
||||
this.ent.destroy()
|
||||
}
|
||||
}
|
||||
// 动画帧事件 atk:仅负责开启一帧碰撞窗口,不负责伤害与计数
|
||||
public atk(args:any){
|
||||
if(!this.SConf) return;
|
||||
if(this.SConf.EType==EType.collision) return
|
||||
public atk(args: any) {
|
||||
if (!this.SConf) return;
|
||||
if (this.endType == EType.collision) return
|
||||
if (this.enable_collider_safely()) {
|
||||
mLogger.log(this.debugMode, 'SkillView', `[SkillView] [${this.SConf?.name}] 开启碰撞检测`);
|
||||
this.scheduleOnce(() => {
|
||||
@@ -179,7 +205,7 @@ export class SkillView extends CCComp {
|
||||
finalRatio,
|
||||
);
|
||||
}
|
||||
close_collider(){
|
||||
close_collider() {
|
||||
if (!this.collider) return;
|
||||
if (this.pendingDisableCollider && !this.collider.enabled) return;
|
||||
this.pendingDisableCollider = true;
|
||||
|
||||
@@ -19,8 +19,6 @@ export class TooltipCom extends CCComp {
|
||||
@property(Node)
|
||||
fstart_bg: Node = null;
|
||||
@property(Node)
|
||||
fend_bg: Node = null;
|
||||
@property(Node)
|
||||
dead_bg: Node = null;
|
||||
@property(Node)
|
||||
revive_bg: Node = null;
|
||||
@@ -146,10 +144,10 @@ export class TooltipCom extends CCComp {
|
||||
this.node.setPosition(v3(this.node.position.x, currentY - 60));
|
||||
this.node.setSiblingIndex(topSiblingIndex);
|
||||
break;
|
||||
case TooltipTypes.shield: // 护盾格挡
|
||||
case TooltipTypes.shield: // 护盾减免
|
||||
this.node.setPosition(v3(this.node.position.x + offsetX, currentY));
|
||||
this.node.setSiblingIndex(topSiblingIndex);
|
||||
this.setupLabel("add_life", "hp", "格挡");
|
||||
this.setupLabel("add_life", "hp", "减免");
|
||||
isHeal = true;
|
||||
break;
|
||||
}
|
||||
@@ -176,7 +174,6 @@ export class TooltipCom extends CCComp {
|
||||
this.atking_bg && (this.atking_bg.active = false);
|
||||
this.atked_bg && (this.atked_bg.active = false);
|
||||
this.fstart_bg && (this.fstart_bg.active = false);
|
||||
this.fend_bg && (this.fend_bg.active = false);
|
||||
this.dead_bg && (this.dead_bg.active = false);
|
||||
this.revive_bg && (this.revive_bg.active = false);
|
||||
|
||||
@@ -186,7 +183,6 @@ export class TooltipCom extends CCComp {
|
||||
[SkillTriggerType.Atking]: this.atking_bg,
|
||||
[SkillTriggerType.Atked]: this.atked_bg,
|
||||
[SkillTriggerType.FStart]: this.fstart_bg,
|
||||
[SkillTriggerType.FEnd]: this.fend_bg,
|
||||
[SkillTriggerType.Dead]: this.dead_bg,
|
||||
[SkillTriggerType.Revive]: this.revive_bg,
|
||||
};
|
||||
|
||||
83
tools/sync-skill-prefab.js
Normal file
83
tools/sync-skill-prefab.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 一次性迁移脚本:把 SkillSet 中的 RType/EType/speed/time 刷入各技能 prefab 的 SkillView 序列化字段。
|
||||
* 用法: node tools/sync-skill-prefab.js
|
||||
* 注意: 直接改写 prefab JSON,运行前请保证工作区已提交。
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const SKILL_SET = path.join(ROOT, 'assets/script/game/common/config/SkillSet.ts');
|
||||
const PREFAB_DIR = path.join(ROOT, 'assets/resources/game/skill/atk');
|
||||
// SkillView 组件在 prefab 中的脚本类 cid
|
||||
const SKILL_VIEW_CID = '57aabs7TE1J5obTAZczc+64';
|
||||
|
||||
const RTYPE = { linear: 0, bezier: 1, fixed: 2, fixedEnd: 3 };
|
||||
const ETYPE = { animationEnd: 0, timeEnd: 1, collision: 2 };
|
||||
|
||||
/** 从 SkillSet.ts 解析 sp_name -> {runType, endType, speed, time} */
|
||||
function parseSkillSet(src) {
|
||||
const map = new Map();
|
||||
// 匹配每个技能条目块
|
||||
const entryRe = /\d+:\s*\{([\s\S]*?)\n\s*\},/g;
|
||||
let m;
|
||||
while ((m = entryRe.exec(src)) !== null) {
|
||||
const body = m[1];
|
||||
const sp = body.match(/sp_name:\s*"([^"]+)"/);
|
||||
if (!sp) continue;
|
||||
const spName = sp[1];
|
||||
|
||||
const rt = body.match(/RType:\s*RType\.(\w+)/);
|
||||
const et = body.match(/EType:\s*EType\.(\w+)/);
|
||||
const speed = body.match(/speed:\s*([\d.]+)/);
|
||||
const time = body.match(/\btime:\s*([\d.]+)/);
|
||||
|
||||
const entry = {
|
||||
runType: rt ? RTYPE[rt[1]] : undefined,
|
||||
endType: et ? ETYPE[et[1]] : undefined,
|
||||
speed: speed ? Number(speed[1]) : undefined,
|
||||
time: time ? Number(time[1]) : 0,
|
||||
};
|
||||
// 多个技能可共用同一 sp_name,保留首个非 undefined 组合
|
||||
if (!map.has(spName)) map.set(spName, entry);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const src = fs.readFileSync(SKILL_SET, 'utf8');
|
||||
const confMap = parseSkillSet(src);
|
||||
|
||||
const files = fs.readdirSync(PREFAB_DIR).filter(f => f.endsWith('.prefab'));
|
||||
const report = { updated: [], skipped: [], noConf: [] };
|
||||
|
||||
for (const file of files) {
|
||||
const spName = path.basename(file, '.prefab');
|
||||
const conf = confMap.get(spName);
|
||||
const fp = path.join(PREFAB_DIR, file);
|
||||
const json = JSON.parse(fs.readFileSync(fp, 'utf8'));
|
||||
|
||||
// 定位 SkillView 组件
|
||||
const comp = json.find(o => o && o.__type__ === SKILL_VIEW_CID);
|
||||
if (!comp) { report.skipped.push(file + ' (no SkillView)'); continue; }
|
||||
|
||||
if (!conf) { report.noConf.push(file); continue; }
|
||||
|
||||
if (conf.runType !== undefined) comp.runType = conf.runType;
|
||||
if (conf.endType !== undefined) comp.endType = conf.endType;
|
||||
if (conf.speed !== undefined) comp.speed = conf.speed;
|
||||
comp.time = conf.time ?? 0;
|
||||
|
||||
fs.writeFileSync(fp, JSON.stringify(json, null, 2) + '\n', 'utf8');
|
||||
report.updated.push(`${file} -> runType=${comp.runType} endType=${comp.endType} speed=${comp.speed} time=${comp.time}`);
|
||||
}
|
||||
|
||||
console.log('=== updated ===');
|
||||
report.updated.forEach(s => console.log(' ' + s));
|
||||
console.log('=== no config matched ===');
|
||||
report.noConf.forEach(s => console.log(' ' + s));
|
||||
console.log('=== skipped ===');
|
||||
report.skipped.forEach(s => console.log(' ' + s));
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user