diff --git a/assets/bundles/battleGameMonster/prefab/怪_中毒.prefab b/assets/bundles/battleGameMonster/prefab/怪_中毒.prefab new file mode 100644 index 0000000..0724254 --- /dev/null +++ b/assets/bundles/battleGameMonster/prefab/怪_中毒.prefab @@ -0,0 +1,262 @@ +[ + { + "__type__": "cc.Prefab", + "_name": "怪_中毒", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "data": { + "__id__": 1 + }, + "optimizationPolicy": 0, + "persistent": false + }, + { + "__type__": "cc.Node", + "_name": "怪_中毒", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + } + ], + "_active": true, + "_components": [ + { + "__id__": 10 + } + ], + "_prefab": { + "__id__": 12 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "anim", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 3 + }, + { + "__id__": 5 + }, + { + "__id__": 7 + } + ], + "_prefab": { + "__id__": 9 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 40.832, + "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": 33554432, + "_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": 105.44999694824219, + "height": 198.25999450683594 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.4653390338067923, + "y": 0.22924443671046482 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "03h5CL5PFEtKYvKtyo9JFN" + }, + { + "__type__": "sp.Skeleton", + "_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 + }, + "_skeletonData": { + "__uuid__": "25a41a00-4bf7-4021-87d4-f8c80309ec53", + "__expectedType__": "sp.SkeletonData" + }, + "defaultSkin": "default", + "defaultAnimation": "animation", + "_premultipliedAlpha": false, + "_timeScale": 1, + "_preCacheMode": 2, + "_cacheMode": 2, + "_sockets": [], + "_useTint": false, + "_debugMesh": false, + "_debugBones": false, + "_debugSlots": false, + "_enableBatch": true, + "loop": true, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "3dGN5uYgVBA5YfZxnqBCr0" + }, + { + "__type__": "cc.Sorting2D", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": { + "__id__": 8 + }, + "_sortingLayer": 4, + "_sortingOrder": 2, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "3eg+71aSNIHpRSdR16zp+r" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "b2X8MHIMdL4oWgMCIc8jCb", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 11 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 100, + "height": 100 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "8fKe6v5I1Jeq7/0RxYFv/t" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "ccwpiVeu1GJohxospbaLDW", + "instance": null, + "targetOverrides": null + } +] \ No newline at end of file diff --git a/assets/bundles/battleGameMonster/prefab/怪_中毒.prefab.meta b/assets/bundles/battleGameMonster/prefab/怪_中毒.prefab.meta new file mode 100644 index 0000000..197e667 --- /dev/null +++ b/assets/bundles/battleGameMonster/prefab/怪_中毒.prefab.meta @@ -0,0 +1,13 @@ +{ + "ver": "1.1.50", + "importer": "prefab", + "imported": true, + "uuid": "27d71c32-ea24-42f6-bc98-4b6d07251929", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": { + "syncNodeName": "怪_中毒" + } +} diff --git a/assets/bundles/battleHero/prefab/skill/冰球.prefab b/assets/bundles/battleHero/prefab/skill/冰球.prefab new file mode 100644 index 0000000..d02ec98 --- /dev/null +++ b/assets/bundles/battleHero/prefab/skill/冰球.prefab @@ -0,0 +1,262 @@ +[ + { + "__type__": "cc.Prefab", + "_name": "冰球", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "data": { + "__id__": 1 + }, + "optimizationPolicy": 0, + "persistent": false + }, + { + "__type__": "cc.Node", + "_name": "冰球", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + } + ], + "_active": true, + "_components": [ + { + "__id__": 10 + } + ], + "_prefab": { + "__id__": 12 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "" + }, + { + "__type__": "cc.Node", + "_name": "冰球陨石", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 3 + }, + { + "__id__": 5 + }, + { + "__id__": 7 + } + ], + "_prefab": { + "__id__": 9 + }, + "_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": 33554432, + "_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": 622.1900024414062, + "height": 413.239990234375 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.10895385758293562, + "y": 0.44748814862778713 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "179HuDnA9Jo7TgGvC9xZhc" + }, + { + "__type__": "sp.Skeleton", + "_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 + }, + "_skeletonData": { + "__uuid__": "781dbed2-3bf9-4e54-bdba-cb12c23529ec", + "__expectedType__": "sp.SkeletonData" + }, + "defaultSkin": "default", + "defaultAnimation": "animation", + "_premultipliedAlpha": false, + "_timeScale": 1, + "_preCacheMode": 0, + "_cacheMode": 0, + "_sockets": [], + "_useTint": false, + "_debugMesh": false, + "_debugBones": false, + "_debugSlots": false, + "_enableBatch": false, + "loop": true, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "a8P1wKkP9DwKwfmMn/qsjD" + }, + { + "__type__": "cc.Sorting2D", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": { + "__id__": 8 + }, + "_sortingLayer": 4, + "_sortingOrder": 0, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "22RwYWwElLeJ6kJ0xw9tQg" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "f5NqVBVAFNbqc4oknHHrnQ", + "instance": null, + "targetOverrides": null, + "nestedPrefabInstanceRoots": null + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 1 + }, + "_enabled": true, + "__prefab": { + "__id__": 11 + }, + "_contentSize": { + "__type__": "cc.Size", + "width": 200, + "height": 200 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "" + }, + { + "__type__": "cc.CompPrefabInfo", + "fileId": "451RzMpQpFLalWlPKOafD2" + }, + { + "__type__": "cc.PrefabInfo", + "root": { + "__id__": 1 + }, + "asset": { + "__id__": 0 + }, + "fileId": "38JEit66ZI+Y7iGQ+17T7W", + "instance": null, + "targetOverrides": null + } +] \ No newline at end of file diff --git a/assets/bundles/battleHero/prefab/skill/冰球.prefab.meta b/assets/bundles/battleHero/prefab/skill/冰球.prefab.meta new file mode 100644 index 0000000..57344b0 --- /dev/null +++ b/assets/bundles/battleHero/prefab/skill/冰球.prefab.meta @@ -0,0 +1,13 @@ +{ + "ver": "1.1.50", + "importer": "prefab", + "imported": true, + "uuid": "e5090e75-028c-4066-87d5-ab02f82fcaff", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": { + "syncNodeName": "冰球" + } +} diff --git a/assets/mx/module/gm/GmEasy.ts b/assets/mx/module/gm/GmEasy.ts index 3c81f2c..4dc6ef9 100644 --- a/assets/mx/module/gm/GmEasy.ts +++ b/assets/mx/module/gm/GmEasy.ts @@ -143,8 +143,10 @@ export class GmEasy { /** * 当前 Tab 直接嵌入内容面板(整页展示,无列表入口)。 + * @param panelId 已注册面板 id + * @param panelData 面板数据(可选) */ - embed(panelId: string): this { + embed(panelId: string, panelData?: unknown): this { this._push({ id: this._id('embed'), title: this._group, @@ -152,6 +154,7 @@ export class GmEasy { kind: GmKind.Content, order: ++this._order, panelId, + panelData, embed: true, }); return this; diff --git a/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.atlas b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.atlas new file mode 100644 index 0000000..20668cb --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.atlas @@ -0,0 +1,139 @@ + +中毒效果.png +size: 698,329 +format: RGBA8888 +filter: Linear,Linear +repeat: none +无标题1 + rotate: false + xy: 601, 11 + size: 69, 87 + orig: 74, 92 + offset: 2, 3 + index: -1 +无标题10 + rotate: false + xy: 103, 156 + size: 77, 173 + orig: 81, 178 + offset: 2, 3 + index: -1 +无标题11 + rotate: false + xy: 263, 159 + size: 77, 170 + orig: 81, 176 + offset: 2, 3 + index: -1 +无标题12 + rotate: false + xy: 451, 4 + size: 76, 161 + orig: 81, 167 + offset: 2, 3 + index: -1 +无标题13 + rotate: false + xy: 222, 4 + size: 74, 151 + orig: 79, 157 + offset: 3, 3 + index: -1 +无标题14 + rotate: false + xy: 376, 27 + size: 73, 131 + orig: 77, 136 + offset: 2, 3 + index: -1 +无标题15 + rotate: false + xy: 342, 160 + size: 74, 169 + orig: 78, 175 + offset: 2, 3 + index: -1 +无标题16 + rotate: false + xy: 418, 167 + size: 74, 162 + orig: 79, 166 + offset: 3, 2 + index: -1 +无标题17 + rotate: false + xy: 150, 0 + size: 70, 154 + orig: 74, 160 + offset: 2, 3 + index: -1 +无标题18 + rotate: false + xy: 529, 7 + size: 70, 91 + orig: 74, 96 + offset: 2, 2 + index: -1 +无标题2 + rotate: false + xy: 494, 198 + size: 71, 131 + orig: 77, 137 + offset: 3, 4 + index: -1 +无标题3 + rotate: true + xy: 567, 260 + size: 69, 131 + orig: 73, 136 + offset: 2, 3 + index: -1 +无标题4 + rotate: false + xy: 0, 1 + size: 72, 135 + orig: 76, 139 + offset: 2, 2 + index: -1 +无标题5 + rotate: false + xy: 74, 3 + size: 74, 133 + orig: 79, 138 + offset: 2, 3 + index: -1 +无标题6 + rotate: false + xy: 298, 25 + size: 76, 132 + orig: 80, 138 + offset: 2, 3 + index: -1 +无标题7 + rotate: true + xy: 567, 180 + size: 78, 130 + orig: 83, 135 + offset: 2, 2 + index: -1 +无标题8 + rotate: true + xy: 529, 100 + size: 78, 123 + orig: 83, 128 + offset: 2, 3 + index: -1 +无标题9 + rotate: false + xy: 182, 157 + size: 79, 172 + orig: 83, 178 + offset: 2, 3 + index: -1 +毒雾 + rotate: false + xy: 0, 138 + size: 101, 191 + orig: 101, 196 + offset: 0, 5 + index: -1 diff --git a/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.atlas.meta b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.atlas.meta new file mode 100644 index 0000000..6df6076 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.atlas.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.0.0", + "importer": "*", + "imported": true, + "uuid": "c7574f4d-50fa-49f3-9ffd-f193fac7ee9c", + "files": [ + ".atlas", + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.json b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.json new file mode 100644 index 0000000..7d45947 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.json @@ -0,0 +1 @@ +{"skeleton":{"hash":"ae4vm7m4zHliUH2j9YM0oDF2390","spine":"3.8.75","x":-49.07,"y":-45.45,"width":105.45,"height":198.26,"images":"./images/","audio":"E:/工作文件/1项目汇总/5战斗游戏/【新】数字大作战/动效/局内效果/毒物"},"bones":[{"name":"root"},{"name":"毒物","parent":"root","length":38.78,"rotation":0.83,"x":1.53,"y":-13.42,"scaleY":0.8306},{"name":"毒雾","parent":"root","length":71.22,"rotation":89.76,"x":3.43,"y":-14.19},{"name":"毒雾2","parent":"root","length":71.22,"rotation":89.76,"x":3.43,"y":-14.19,"scaleY":-1}],"slots":[{"name":"毒物","bone":"毒物","attachment":"无标题1"},{"name":"毒雾","bone":"毒雾","attachment":"毒雾"},{"name":"毒雾2","bone":"毒雾2","attachment":"毒雾"}],"skins":[{"name":"default","attachments":{"毒雾2":{"毒雾":{"x":67.87,"y":-0.05,"rotation":-88.93,"width":101,"height":196}},"毒物":{"无标题1":{"x":0.68,"y":32.14,"rotation":-0.95,"width":74,"height":92},"无标题2":{"x":-0.06,"y":15.65,"rotation":-0.83,"width":77,"height":137},"无标题3":{"x":1.01,"y":20.64,"rotation":-0.83,"width":73,"height":136},"无标题4":{"x":-0.39,"y":27.16,"rotation":-0.83,"width":76,"height":139},"无标题5":{"x":0.21,"y":34.15,"rotation":-0.83,"width":79,"height":138},"无标题6":{"x":0.79,"y":40.14,"rotation":-0.83,"width":80,"height":138},"无标题7":{"x":1.39,"y":46.64,"rotation":-0.83,"width":83,"height":135},"无标题8":{"x":1.48,"y":53.13,"rotation":-0.83,"width":83,"height":128},"无标题9":{"x":2.17,"y":31.62,"rotation":-0.83,"width":83,"height":178},"无标题10":{"x":0.21,"y":34.15,"rotation":-0.83,"width":81,"height":178},"无标题11":{"x":0.27,"y":38.65,"rotation":-0.83,"width":81,"height":176},"无标题12":{"x":0.37,"y":45.65,"rotation":-0.83,"width":81,"height":167},"无标题13":{"x":-0.54,"y":51.66,"rotation":-0.83,"width":79,"height":157},"无标题14":{"x":-0.5,"y":54.16,"rotation":-0.83,"width":77,"height":136},"无标题15":{"x":-0.23,"y":38.16,"rotation":-0.83,"width":78,"height":175},"无标题16":{"x":0.36,"y":44.65,"rotation":-0.83,"width":79,"height":166},"无标题17":{"x":-0.06,"y":50.16,"rotation":-0.83,"width":74,"height":160},"无标题18":{"x":0.59,"y":26.14,"rotation":-0.83,"width":74,"height":96}},"毒雾":{"毒雾":{"x":67.87,"y":-0.05,"rotation":-88.93,"width":101,"height":196}}}}],"animations":{"animation":{"slots":{"毒雾":{"color":[{"color":"ffffff00"},{"time":0.6,"color":"ffffffff"},{"time":1.2333,"color":"ffffff00"}]},"毒雾2":{"color":[{"color":"ffffffff"},{"time":0.6333,"color":"ffffff00"},{"time":1.2333,"color":"ffffffff"}]},"毒物":{"attachment":[{"time":0.0667,"name":"无标题2"},{"time":0.1333,"name":"无标题3"},{"time":0.2,"name":"无标题4"},{"time":0.2667,"name":"无标题5"},{"time":0.3333,"name":"无标题6"},{"time":0.4,"name":"无标题7"},{"time":0.4667,"name":"无标题8"},{"time":0.5333,"name":"无标题9"},{"time":0.6,"name":"无标题10"},{"time":0.7,"name":"无标题11"},{"time":0.7667,"name":"无标题12"},{"time":0.8333,"name":"无标题13"},{"time":0.9,"name":"无标题14"},{"time":0.9667,"name":"无标题15"},{"time":1.0667,"name":"无标题16"},{"time":1.1333,"name":"无标题17"},{"time":1.2,"name":"无标题18"}]}},"bones":{"毒雾":{"translate":[{"x":-1.58,"y":-23.25}],"scale":[{"x":0.752},{"time":1.2},{"time":1.2333,"x":0.752}]},"毒雾2":{"translate":[{"x":-1.58,"y":-23.25}],"scale":[{"x":0.876},{"time":0.6},{"time":0.6333,"x":0.752},{"time":1.2333,"x":0.876}]}}}}} \ No newline at end of file diff --git a/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.json.meta b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.json.meta new file mode 100644 index 0000000..9e28856 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.json.meta @@ -0,0 +1,13 @@ +{ + "ver": "1.2.7", + "importer": "spine-data", + "imported": true, + "uuid": "25a41a00-4bf7-4021-87d4-f8c80309ec53", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": { + "atlasUuid": "c7574f4d-50fa-49f3-9ffd-f193fac7ee9c" + } +} diff --git a/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.png b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.png new file mode 100644 index 0000000..56c0dea Binary files /dev/null and b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.png differ diff --git a/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.png.meta b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.png.meta new file mode 100644 index 0000000..f7da695 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/BUFF/中毒效果.png.meta @@ -0,0 +1,134 @@ +{ + "ver": "1.0.27", + "importer": "image", + "imported": true, + "uuid": "12a7084a-71ac-476f-8fdd-c6ec2a3dd801", + "files": [ + ".json", + ".png" + ], + "subMetas": { + "6c48a": { + "importer": "texture", + "uuid": "12a7084a-71ac-476f-8fdd-c6ec2a3dd801@6c48a", + "displayName": "中毒效果", + "id": "6c48a", + "name": "texture", + "userData": { + "wrapModeS": "clamp-to-edge", + "wrapModeT": "clamp-to-edge", + "imageUuidOrDatabaseUri": "12a7084a-71ac-476f-8fdd-c6ec2a3dd801", + "isUuid": true, + "visible": false, + "minfilter": "linear", + "magfilter": "linear", + "mipfilter": "none", + "anisotropy": 0 + }, + "ver": "1.0.22", + "imported": true, + "files": [ + ".json" + ], + "subMetas": {} + }, + "f9941": { + "importer": "sprite-frame", + "uuid": "12a7084a-71ac-476f-8fdd-c6ec2a3dd801@f9941", + "displayName": "中毒效果", + "id": "f9941", + "name": "spriteFrame", + "userData": { + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 698, + "height": 329, + "rawWidth": 698, + "rawHeight": 329, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "packable": true, + "pixelsToUnit": 100, + "pivotX": 0.5, + "pivotY": 0.5, + "meshType": 0, + "vertices": { + "rawPosition": [ + -349, + -164.5, + 0, + 349, + -164.5, + 0, + -349, + 164.5, + 0, + 349, + 164.5, + 0 + ], + "indexes": [ + 0, + 1, + 2, + 2, + 1, + 3 + ], + "uv": [ + 0, + 329, + 698, + 329, + 0, + 0, + 698, + 0 + ], + "nuv": [ + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1 + ], + "minPos": [ + -349, + -164.5, + 0 + ], + "maxPos": [ + 349, + 164.5, + 0 + ] + }, + "isUuid": true, + "imageUuidOrDatabaseUri": "12a7084a-71ac-476f-8fdd-c6ec2a3dd801@6c48a", + "atlasUuid": "", + "trimType": "auto" + }, + "ver": "1.0.12", + "imported": true, + "files": [ + ".json" + ], + "subMetas": {} + } + }, + "userData": { + "type": "sprite-frame", + "fixAlphaTransparencyArtifacts": false, + "hasAlpha": true, + "redirect": "12a7084a-71ac-476f-8fdd-c6ec2a3dd801@6c48a" + } +} diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球.meta b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球.meta new file mode 100644 index 0000000..0d2f442 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "6f2bef0b-9419-4eee-84fb-01fd55a1715e", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/.WeDrive b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/.WeDrive new file mode 100644 index 0000000..f16bf61 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/.WeDrive @@ -0,0 +1 @@ +D:\WXWork\1688855084654441\WeDrive\中昱智云\抖音游戏\战斗类\【新】扑克牌请神\动效\局内效果\陨落冰球 \ No newline at end of file diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.atlas b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.atlas new file mode 100644 index 0000000..b8a85a5 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.atlas @@ -0,0 +1,139 @@ + +冰球陨石.png +size: 650,518 +format: RGBA8888 +filter: Linear,Linear +repeat: none +冰球 + rotate: true + xy: 528, 394 + size: 124, 119 + orig: 126, 121 + offset: 1, 1 + index: -1 +冲击波 + rotate: false + xy: 0, 299 + size: 526, 219 + orig: 535, 237 + offset: 3, 7 + index: -1 +图层 1179 + rotate: false + xy: 410, 265 + size: 29, 32 + orig: 31, 34 + offset: 1, 1 + index: -1 +图层 1180 + rotate: true + xy: 603, 255 + size: 50, 45 + orig: 52, 47 + offset: 1, 1 + index: -1 +图层 1181 + rotate: false + xy: 441, 261 + size: 12, 15 + orig: 14, 17 + offset: 1, 1 + index: -1 +图层 1182 + rotate: true + xy: 441, 278 + size: 19, 17 + orig: 21, 19 + offset: 1, 1 + index: -1 +图层 1183 + rotate: false + xy: 455, 264 + size: 10, 12 + orig: 12, 14 + offset: 1, 1 + index: -1 +图层 1184 + rotate: false + xy: 291, 261 + size: 43, 36 + orig: 45, 38 + offset: 1, 1 + index: -1 +图层 1185 + rotate: false + xy: 609, 354 + size: 41, 38 + orig: 43, 40 + offset: 1, 1 + index: -1 +图层 1186 + rotate: true + xy: 420, 41 + size: 62, 57 + orig: 65, 59 + offset: 1, 1 + index: -1 +图层 1187 + rotate: false + xy: 479, 51 + size: 52, 52 + orig: 54, 54 + offset: 1, 1 + index: -1 +图层 1188 + rotate: true + xy: 336, 264 + size: 33, 44 + orig: 35, 46 + offset: 1, 1 + index: -1 +图层 1189 + rotate: false + xy: 382, 264 + size: 26, 33 + orig: 29, 35 + offset: 2, 1 + index: -1 +图层 1190 + rotate: true + xy: 528, 329 + size: 63, 79 + orig: 65, 81 + offset: 1, 1 + index: -1 +图层 1191 + rotate: false + xy: 528, 261 + size: 73, 66 + orig: 75, 71 + offset: 1, 4 + index: -1 +图层 1192 + rotate: false + xy: 609, 307 + size: 40, 45 + orig: 56, 47 + offset: 15, 1 + index: -1 +拖尾 + rotate: true + xy: 420, 105 + size: 148, 224 + orig: 152, 239 + offset: 2, 9 + index: -1 +法阵11 + rotate: true + xy: 0, 3 + size: 294, 289 + orig: 296, 290 + offset: 1, 0 + index: -1 +裂隙 + rotate: true + xy: 291, 0 + size: 253, 127 + orig: 272, 130 + offset: 18, 1 + index: -1 diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.atlas.meta b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.atlas.meta new file mode 100644 index 0000000..c7d635a --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.atlas.meta @@ -0,0 +1,12 @@ +{ + "ver": "1.0.0", + "importer": "*", + "imported": true, + "uuid": "0569be0e-0391-4613-a3c9-3ef1407de3c7", + "files": [ + ".atlas", + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.json b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.json new file mode 100644 index 0000000..d3ba1d3 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.json @@ -0,0 +1 @@ +{"skeleton":{"hash":"3+YV1HSkIBQ1LfVjDPJyvt3oIo8","spine":"3.8.75","x":-67.79,"y":-184.92,"width":622.19,"height":413.24,"images":"./images/","audio":"E:/工作文件/1项目汇总/5战斗游戏/【新】数字大作战/动效/局内效果/冰球陨石"},"bones":[{"name":"root"},{"name":"冰球","parent":"root","length":42.14,"rotation":-90.58,"x":4.53,"y":-2.89},{"name":"拖尾","parent":"冰球","length":73.84,"rotation":161.77,"x":-1.6,"y":-0.27},{"name":"拖尾2","parent":"冰球","length":73.84,"rotation":161.12,"x":-1.3,"y":1.28,"scaleY":-1},{"name":"bone","parent":"root","x":-415.87,"y":186.46},{"name":"图层 1186","parent":"bone","length":16.65,"x":19.69,"y":-2.09},{"name":"图层 1187","parent":"bone","length":15.73,"rotation":38.66,"x":55.73,"y":27.66},{"name":"图层 1189","parent":"bone","length":14.25,"rotation":69.83,"x":18.05,"y":46.5},{"name":"图层 1188","parent":"bone","length":15.21,"rotation":68.96,"x":38.53,"y":79.53},{"name":"图层 1190","parent":"bone","length":20.03,"rotation":107.45,"x":-18.53,"y":43.77},{"name":"图层 1179","parent":"bone","length":11.43,"rotation":130.16,"x":-68.22,"y":59.88},{"name":"图层 1191","parent":"bone","length":22.93,"rotation":180,"x":-31.09,"y":-7.01},{"name":"图层 1183","parent":"bone","length":12,"rotation":-107.2,"x":-10.89,"y":-67.61},{"name":"图层 1185","parent":"bone","length":16.58,"rotation":-107.24,"x":-8.43,"y":-34.85},{"name":"图层 1184","parent":"bone","length":20.21,"rotation":-141.58,"x":-43.1,"y":-52.05},{"name":"图层 1192","parent":"bone","length":19.2,"rotation":-75.17,"x":25.42,"y":-56.97},{"name":"图层 1181","parent":"bone","length":13.6,"rotation":-51.52,"x":66.1,"y":-58.33},{"name":"图层 1180","parent":"bone","length":19.93,"rotation":-23.4,"x":62.55,"y":-23.66},{"name":"图层 1182","parent":"bone","length":17.19,"rotation":-3.65,"x":71.1,"y":4.52},{"name":"冲击波","parent":"root","length":68,"rotation":178.17,"x":1.76,"y":-24.32},{"name":"法阵11","parent":"root","x":346.65,"y":23.94},{"name":"法阵13","parent":"法阵11","length":56.78,"rotation":-0.75,"x":1.3,"y":-1.77},{"name":"bone2","parent":"root","x":120.52,"y":769.24}],"slots":[{"name":"裂隙","bone":"冲击波"},{"name":"冲击波","bone":"冲击波"},{"name":"图层 1179","bone":"图层 1179"},{"name":"图层 1192","bone":"图层 1192"},{"name":"图层 1191","bone":"图层 1191"},{"name":"图层 1190","bone":"图层 1190"},{"name":"图层 1189","bone":"图层 1189"},{"name":"图层 1188","bone":"图层 1188"},{"name":"图层 1187","bone":"图层 1187"},{"name":"图层 1186","bone":"图层 1186"},{"name":"图层 1185","bone":"图层 1185"},{"name":"图层 1184","bone":"图层 1184"},{"name":"图层 1183","bone":"图层 1183"},{"name":"图层 1182","bone":"图层 1182"},{"name":"图层 1181","bone":"图层 1181"},{"name":"图层 1180","bone":"图层 1180"},{"name":"root221","bone":"bone2","attachment":"root221"},{"name":"拖尾","bone":"拖尾","attachment":"拖尾"},{"name":"拖尾2","bone":"拖尾2","attachment":"拖尾"},{"name":"冰球","bone":"冰球","attachment":"冰球"},{"name":"法阵11","bone":"法阵13","attachment":"法阵11"}],"skins":[{"name":"default","attachments":{"拖尾2":{"拖尾":{"x":45.71,"y":11.79,"rotation":-71.67,"width":152,"height":239}},"root221":{"root221":{"type":"clipping","end":"冰球","vertexCount":7,"vertices":[19.86,-740.82,245.38,51.18,130.52,134.15,92.58,150.72,49.02,161.83,-51.28,177.83,-519.19,-586.62],"color":"ce3a3aff"}},"图层 1190":{"图层 1190":{"x":10.3,"y":3.55,"rotation":-107.45,"width":65,"height":81}},"图层 1191":{"图层 1191":{"x":18.92,"y":-1.54,"rotation":180,"width":75,"height":71}},"图层 1192":{"图层 1192":{"x":4.31,"y":-5.2,"rotation":75.17,"width":56,"height":47}},"裂隙":{"裂隙":{"x":-5.47,"y":12.85,"rotation":8.52,"width":272,"height":130}},"图层 1179":{"图层 1179":{"x":7.8,"y":-0.31,"rotation":-130.16,"width":31,"height":34}},"法阵11":{"法阵11":{"x":0.2,"y":-0.46,"rotation":130.91,"width":296,"height":290}},"冰球":{"冰球":{"x":-7.39,"y":0.39,"rotation":90.58,"width":126,"height":121}},"拖尾":{"拖尾":{"x":45.71,"y":11.79,"rotation":-71.67,"width":152,"height":239}},"图层 1185":{"图层 1185":{"x":3.73,"y":-1.54,"rotation":107.24,"width":43,"height":40}},"图层 1180":{"图层 1180":{"x":5.25,"y":0.31,"rotation":23.4,"width":52,"height":47}},"图层 1181":{"图层 1181":{"x":2.54,"y":-0.23,"rotation":51.52,"width":14,"height":17}},"图层 1182":{"图层 1182":{"x":-0.3,"y":-1.2,"rotation":3.65,"width":21,"height":19}},"图层 1183":{"图层 1183":{"x":1.47,"y":-0.19,"rotation":107.2,"width":12,"height":14}},"图层 1184":{"图层 1184":{"x":10.65,"y":-0.9,"rotation":141.58,"width":45,"height":38}},"冲击波":{"冲击波":{"x":-5.49,"y":35.99,"rotation":-178.17,"width":535,"height":237}},"图层 1186":{"图层 1186":{"x":1.31,"y":-3.37,"width":65,"height":59}},"图层 1187":{"图层 1187":{"x":-1.67,"y":3.09,"rotation":-38.66,"width":54,"height":54}},"图层 1188":{"图层 1188":{"x":1.57,"y":0.1,"rotation":-68.96,"width":35,"height":46}},"图层 1189":{"图层 1189":{"x":0.36,"y":-0.87,"rotation":-69.83,"width":29,"height":35}}}}],"animations":{"animation":{"slots":{"图层 1180":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1180"}]},"图层 1187":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1187"}]},"图层 1182":{"color":[{"time":1.4667,"color":"ffffffff"},{"time":1.6,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1182"}]},"图层 1192":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1192"}]},"冲击波":{"color":[{"time":1.2667,"color":"ffffffff"},{"time":1.4,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"冲击波"}]},"法阵11":{"color":[{"color":"ffffff00"},{"time":0.3667,"color":"ffffffba","curve":"stepped"},{"time":0.7,"color":"ffffffba"},{"time":1,"color":"ffffff00"}]},"图层 1190":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1190"}]},"图层 1183":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1183"}]},"图层 1188":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1188"}]},"图层 1191":{"color":[{"time":1.4333,"color":"ffffffff"},{"time":1.5667,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1191"}]},"冰球":{"attachment":[{"name":null},{"time":0.3333,"name":"冰球"},{"time":1.1667,"name":null}]},"图层 1179":{"color":[{"time":1.5,"color":"ffffffff"},{"time":1.6333,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1179"}]},"图层 1181":{"color":[{"time":1.5,"color":"ffffffff"},{"time":1.6333,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1181"}]},"图层 1184":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1184"}]},"图层 1189":{"color":[{"time":1.4667,"color":"ffffffff"},{"time":1.6,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1189"}]},"图层 1186":{"color":[{"time":1.3667,"color":"ffffffff"},{"time":1.5,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1186"}]},"拖尾":{"color":[{"time":0.3333,"color":"ffffff00"},{"time":0.7,"color":"ffffffff"},{"time":1.1,"color":"ffffff00"}],"attachment":[{"name":null},{"time":0.3333,"name":"拖尾"},{"time":1.1,"name":null}]},"裂隙":{"color":[{"time":1.3333,"color":"ffffffff"},{"time":2,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"裂隙"}]},"图层 1185":{"color":[{"time":1.4667,"color":"ffffffff"},{"time":1.6,"color":"ffffff00"}],"attachment":[{"time":1.1667,"name":"图层 1185"}]},"拖尾2":{"color":[{"time":0.3333,"color":"ffffffff"},{"time":0.7667,"color":"ffffff00"},{"time":1.1,"color":"ffffffff"}],"attachment":[{"name":null},{"time":0.3333,"name":"拖尾"},{"time":1.1,"name":null}]}},"bones":{"拖尾":{"scale":[{"time":0.3333,"x":0.739},{"time":1.0667,"x":1.236,"curve":"stepped"},{"time":1.1,"x":0.739}]},"拖尾2":{"scale":[{"time":0.3333,"x":0.987},{"time":0.7,"x":1.236,"curve":"stepped"},{"time":0.7667,"x":0.739},{"time":1.1,"x":0.987}]},"冰球":{"translate":[{"time":0.3333,"x":274.83,"y":697.16,"curve":0.25,"c3":0.796,"c4":0.69},{"time":1.1667,"x":8.9,"y":24.49}],"scale":[{"time":0.3333,"x":0.868,"y":0.868}]},"bone":{"translate":[{"time":1.1667,"x":411.81,"y":-183.65}]},"图层 1179":{"translate":[{"time":1.1667,"x":18.85,"y":-20.8},{"time":1.6333,"x":-71.61,"y":70.25}],"scale":[{"time":1.1667},{"time":1.6333,"x":0.81,"y":0.81}]},"图层 1180":{"translate":[{"time":1.1667,"x":-32.17,"y":5.2},{"time":1.5,"x":176.64,"y":-82.52}],"scale":[{"time":1.1667},{"time":1.5,"x":0.675,"y":0.675}]},"图层 1181":{"translate":[{"time":1.1667},{"time":1.6333,"x":82.52,"y":-94.8}]},"图层 1182":{"translate":[{"time":1.1667},{"time":1.6,"x":117.3,"y":-7.5}]},"图层 1183":{"translate":[{"time":1.1667},{"time":1.5,"x":-27.28,"y":-91.39}]},"图层 1184":{"translate":[{"time":1.1667,"x":23.72,"y":17.22},{"time":1.5,"x":-87.3,"y":-95.48}],"scale":[{"time":1.1667},{"time":1.5,"x":0.81,"y":0.81}]},"图层 1185":{"translate":[{"time":1.1667},{"time":1.6,"x":-68.88,"y":-178}],"scale":[{"time":1.1667},{"time":1.6,"x":0.81,"y":0.81}]},"图层 1186":{"translate":[{"time":1.1667},{"time":1.5,"x":223.7,"y":-13.64}],"scale":[{"time":1.1667},{"time":1.5,"x":0.675,"y":0.675}]},"图层 1187":{"translate":[{"time":1.1667,"x":-17.22,"y":-20.15},{"time":1.5,"x":102.98,"y":114.58}],"scale":[{"time":1.1667},{"time":1.5,"x":0.675,"y":0.675}]},"图层 1188":{"translate":[{"time":1.1667,"x":-11.7,"y":-32.17},{"time":1.5,"x":57.97,"y":130.94}],"scale":[{"time":1.1667},{"time":1.5,"x":0.81,"y":0.81}]},"图层 1189":{"translate":[{"time":1.1667},{"time":1.6,"x":33.42,"y":108.44}],"scale":[{"time":1.1667},{"time":1.6,"x":0.81,"y":0.81}]},"图层 1190":{"translate":[{"time":1.1667,"x":16.9,"y":-35.1},{"time":1.5,"x":-66.15,"y":141.86}],"scale":[{"time":1.1667},{"time":1.5,"x":0.675,"y":0.675}]},"图层 1191":{"translate":[{"time":1.1667,"x":34.45,"y":-5.2},{"time":1.5667,"x":-166.41,"y":3.41}],"scale":[{"time":1.1667},{"time":1.5667,"x":0.675,"y":0.675}]},"图层 1192":{"translate":[{"time":1.1667,"x":-10.07,"y":22.42},{"time":1.5,"x":38.19,"y":-151.4}],"scale":[{"time":1.1667},{"time":1.5,"x":0.675,"y":0.675}]},"冲击波":{"scale":[{"time":1.1667,"x":0.529,"y":0.529},{"time":1.4,"x":1.488,"y":1.488}]},"法阵13":{"rotate":[{},{"time":1,"angle":-177.68}]},"法阵11":{"rotate":[{"angle":-7.18}],"translate":[{"x":-87.44,"y":618.72}],"scale":[{"x":1.281,"y":0.31}]},"bone2":{"rotate":[{"time":0.3333,"angle":1.16}],"translate":[{"time":0.3333,"x":60,"y":-280.39}]}}}}} \ No newline at end of file diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.json.meta b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.json.meta new file mode 100644 index 0000000..e9c3b84 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.json.meta @@ -0,0 +1,13 @@ +{ + "ver": "1.2.7", + "importer": "spine-data", + "imported": true, + "uuid": "781dbed2-3bf9-4e54-bdba-cb12c23529ec", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": { + "atlasUuid": "0569be0e-0391-4613-a3c9-3ef1407de3c7" + } +} diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.png b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.png new file mode 100644 index 0000000..21ffba3 Binary files /dev/null and b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.png differ diff --git a/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.png.meta b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.png.meta new file mode 100644 index 0000000..9036e15 --- /dev/null +++ b/assets/res/battleGameWeapon/spine/技能特效/陨落冰球/冰球陨石.png.meta @@ -0,0 +1,134 @@ +{ + "ver": "1.0.27", + "importer": "image", + "imported": true, + "uuid": "016b4c5e-2cc0-4f67-92b1-7451af8adac5", + "files": [ + ".json", + ".png" + ], + "subMetas": { + "6c48a": { + "importer": "texture", + "uuid": "016b4c5e-2cc0-4f67-92b1-7451af8adac5@6c48a", + "displayName": "冰球陨石", + "id": "6c48a", + "name": "texture", + "userData": { + "wrapModeS": "clamp-to-edge", + "wrapModeT": "clamp-to-edge", + "imageUuidOrDatabaseUri": "016b4c5e-2cc0-4f67-92b1-7451af8adac5", + "isUuid": true, + "visible": false, + "minfilter": "linear", + "magfilter": "linear", + "mipfilter": "none", + "anisotropy": 0 + }, + "ver": "1.0.22", + "imported": true, + "files": [ + ".json" + ], + "subMetas": {} + }, + "f9941": { + "importer": "sprite-frame", + "uuid": "016b4c5e-2cc0-4f67-92b1-7451af8adac5@f9941", + "displayName": "冰球陨石", + "id": "f9941", + "name": "spriteFrame", + "userData": { + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 650, + "height": 518, + "rawWidth": 650, + "rawHeight": 518, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "packable": true, + "pixelsToUnit": 100, + "pivotX": 0.5, + "pivotY": 0.5, + "meshType": 0, + "vertices": { + "rawPosition": [ + -325, + -259, + 0, + 325, + -259, + 0, + -325, + 259, + 0, + 325, + 259, + 0 + ], + "indexes": [ + 0, + 1, + 2, + 2, + 1, + 3 + ], + "uv": [ + 0, + 518, + 650, + 518, + 0, + 0, + 650, + 0 + ], + "nuv": [ + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1 + ], + "minPos": [ + -325, + -259, + 0 + ], + "maxPos": [ + 325, + 259, + 0 + ] + }, + "isUuid": true, + "imageUuidOrDatabaseUri": "016b4c5e-2cc0-4f67-92b1-7451af8adac5@6c48a", + "atlasUuid": "", + "trimType": "auto" + }, + "ver": "1.0.12", + "imported": true, + "files": [ + ".json" + ], + "subMetas": {} + } + }, + "userData": { + "type": "sprite-frame", + "fixAlphaTransparencyArtifacts": false, + "hasAlpha": true, + "redirect": "016b4c5e-2cc0-4f67-92b1-7451af8adac5@6c48a" + } +} diff --git a/assets/script/battle/BattleService.ts b/assets/script/battle/BattleService.ts index ebb1f8f..1aa5480 100644 --- a/assets/script/battle/BattleService.ts +++ b/assets/script/battle/BattleService.ts @@ -44,6 +44,7 @@ export class BattleService implements IBattleService { this.end(); BattleBuffCarryStore.clear(); BattleBuffSelectService.resetAll(); + this._seedGmCarryBuffs(params.localPlayerId ?? 0); const runtime = createBattleRuntime(params); if (!runtime) { console.error(`[BattleService] start failed level=${params.levelId} rank=${params.playerRankId} opp=${params.opponentRankId}`); @@ -53,6 +54,23 @@ export class BattleService implements IBattleService { return runtime; } + /** + * GM 词条:开战 clear 后按 gg.game.gmCarryBuffIds 灌入本机 CarryStore。 + * @param localPlayerId 本机槽位 + */ + private _seedGmCarryBuffs(localPlayerId: number): void { + const ids = gg.game.gmCarryBuffIds; + if (!ids?.length) return; + const pid = Math.max(0, Math.floor(Number(localPlayerId) || 0)); + let ok = 0; + for (let i = 0; i < ids.length; i++) { + const parsed = BattleBuffCarryStore.appendBuffId(pid, ids[i]); + if (parsed) ok++; + else console.warn('[BattleService] GM 词条灌入失败', ids[i]); + } + console.log(`[BattleService] GM 词条灌入 player=${pid} count=${ok}/${ids.length}`); + } + end(): void { BattleUiPauseGate.clear(); const levelId = this._match?.levelId; diff --git a/assets/script/battle/ai/bridge/AiBattleBridge.ts b/assets/script/battle/ai/bridge/AiBattleBridge.ts index 65d67c7..60ea605 100644 --- a/assets/script/battle/ai/bridge/AiBattleBridge.ts +++ b/assets/script/battle/ai/bridge/AiBattleBridge.ts @@ -401,7 +401,24 @@ export class AiBattleBridge { private _mountAiBuffs(profile: AiMatchProfile, battle: ProjectBattleHandle): void { const aiPid = AiHeadlessBagHost.AI_PLAYER_ID; BattleBuffCarryStore.setFromBuffIds(aiPid, [], []); - aiOp(`AI buff 赛前清空(局内解锁三选一)profilePassive=[${profile.passiveBuffIds.join(',')}]`); + const gmPassive = gg.game.gmAiPassiveBuffIds; + const gmActive = gg.game.gmAiActiveBuffIds; + let seeded = 0; + if (gmActive?.length) { + for (let i = 0; i < gmActive.length; i++) { + if (BattleBuffCarryStore.appendBuffId(aiPid, gmActive[i])) seeded++; + } + } + if (gmPassive?.length) { + for (let i = 0; i < gmPassive.length; i++) { + if (BattleBuffCarryStore.appendBuffId(aiPid, gmPassive[i])) seeded++; + } + } + if (seeded > 0) { + aiOp(`GM 覆盖 AI buff 灌入 count=${seeded} profilePassive=[${profile.passiveBuffIds.join(',')}]`); + } else { + aiOp(`AI buff 赛前清空(局内解锁三选一)profilePassive=[${profile.passiveBuffIds.join(',')}]`); + } getBattleBuffMoveSpeedHost().bind(battle); getBattleBuffUnitAttackSpeedHost().bind(battle); getBattleBuffBaseThunderHost().refreshCarry(battle); diff --git a/assets/script/battle/combat/PassiveSkillRuntime.ts b/assets/script/battle/combat/PassiveSkillRuntime.ts index e679269..884db4e 100644 --- a/assets/script/battle/combat/PassiveSkillRuntime.ts +++ b/assets/script/battle/combat/PassiveSkillRuntime.ts @@ -163,6 +163,10 @@ export const SWORD_WAVE_EVENT = 'SwordWaveApplied'; export const LIGHTNING_CHAIN_EVENT = 'LightningChainApplied'; /** 分裂冰锥表现事件名 */ export const ICE_CONE_SPLIT_EVENT = 'IceConeSplitApplied'; +/** 中毒表现事件名(上毒/刷新) */ +export const POISON_APPLIED_EVENT = 'PoisonApplied'; +/** 中毒清除表现事件名(目标上已无毒 DoT) */ +export const POISON_REMOVED_EVENT = 'PoisonRemoved'; /** 直线火龙预制体宽 100,命中半长(再加半格占地) */ const LINE_DRAGON_HALF_LEN = 50; /** 直线火龙预制体高 30,命中半宽(再加半格占地) */ @@ -198,6 +202,14 @@ export interface SwordArrayCue { radius: number; } +/** 中毒表现 cue */ +export interface PoisonCue { + /** 中毒目标 */ + target: EntityId; + /** 持续秒(刷新时为新剩余时长) */ + duration: number; +} + /** 爆炸火球一枚(径向直线) */ export interface HomingDragonMissile { /** 方向单位 X */ @@ -808,9 +820,14 @@ export class PassiveSkillHost { this._removeHit(b, target); } } + let removed = false; for (let i = this._dots.length - 1; i >= 0; i--) { - if (this._dots[i].target === target) this._dots.splice(i, 1); + if (this._dots[i].target === target) { + this._dots.splice(i, 1); + removed = true; + } } + if (removed) this._syncPoisonFx(target); } /** @@ -1244,7 +1261,7 @@ export class PassiveSkillHost { } /** - * 上毒:立即首跳,再按间隔跳至结束。 + * 上毒:立即首跳,再按间隔跳至结束;并发中毒表现 cue。 * @param caster 施法者 * @param target 目标 * @param skillId Skill.id @@ -1267,10 +1284,12 @@ export class PassiveSkillHost { d.scale = scale; d.left = dur; d.wait = 0; + this._emitPoisonApplied(target); return; } } this._dots.push({ caster, target, skillId, interval, scale, left: dur, wait: 0 }); + this._emitPoisonApplied(target); } /** @@ -2173,7 +2192,9 @@ export class PassiveSkillHost { for (let i = this._dots.length - 1; i >= 0; i--) { const d = this._dots[i]; if (!session.world.isAlive(d.caster) || !session.world.isAlive(d.target)) { + const target = d.target; this._dots.splice(i, 1); + this._syncPoisonFx(target); continue; } d.left -= dt; @@ -2275,14 +2296,61 @@ export class PassiveSkillHost { * @param dot 毒跳 */ private _removeDot(dot: PoisonDot): void { + const target = dot.target; for (let i = this._dots.length - 1; i >= 0; i--) { if (this._dots[i] === dot) { this._dots.splice(i, 1); + this._syncPoisonFx(target); return; } } } + /** + * 目标上剩余毒 DoT 的最大时长。 + * @param target 目标 + */ + private _poisonLeftMax(target: EntityId): number { + let max = 0; + for (let i = 0; i < this._dots.length; i++) { + const d = this._dots[i]; + if (d.target === target && d.left > max) max = d.left; + } + return max; + } + + /** + * 同步中毒表现:有毒则刷新时长,无毒则清除。 + * @param target 目标 + */ + private _syncPoisonFx(target: EntityId): void { + const left = this._poisonLeftMax(target); + if (left > 0) this._emitPoisonApplied(target); + else this._emitPoisonRemoved(target); + } + + /** + * 发出中毒表现 cue(时长取目标上剩余毒最大值)。 + * @param target 中毒目标 + */ + private _emitPoisonApplied(target: EntityId): void { + const session = this._session; + const duration = this._poisonLeftMax(target); + if (!session || !(target > 0) || !(duration > 0)) return; + const cue: PoisonCue = { target, duration }; + session.events.emit(BattleEventFactory.custom(POISON_APPLIED_EVENT, cue)); + } + + /** + * 发出中毒清除 cue。 + * @param target 目标 + */ + private _emitPoisonRemoved(target: EntityId): void { + const session = this._session; + if (!session || !(target > 0)) return; + session.events.emit(BattleEventFactory.custom(POISON_REMOVED_EVENT, { target })); + } + /** 卸下时清攻速/射程 modifier */ private _stripMods(entityId: EntityId): void { const session = this._session; @@ -2300,8 +2368,22 @@ export class PassiveSkillHost { /** 卸下施法者时清其毒 */ private _removeDotsByCaster(caster: EntityId): void { + const touched: EntityId[] = []; for (let i = this._dots.length - 1; i >= 0; i--) { - if (this._dots[i].caster === caster) this._dots.splice(i, 1); + const d = this._dots[i]; + if (d.caster !== caster) continue; + let dup = false; + for (let j = 0; j < touched.length; j++) { + if (touched[j] === d.target) { + dup = true; + break; + } + } + if (!dup) touched.push(d.target); + this._dots.splice(i, 1); + } + for (let i = 0; i < touched.length; i++) { + this._syncPoisonFx(touched[i]); } } diff --git a/assets/script/battle/config/ChapterLevelBuilder.ts b/assets/script/battle/config/ChapterLevelBuilder.ts index 7eeca2b..0fdd033 100644 --- a/assets/script/battle/config/ChapterLevelBuilder.ts +++ b/assets/script/battle/config/ChapterLevelBuilder.ts @@ -281,7 +281,7 @@ export class ChapterLevelBuilder { * - 上阵武器 skill / debuff 关联特效 */ static collectLevelStatusFxPrefabs(levelId: number): string[] { - const prefabs = membershipFromArray(['怪_攻击buff', '怪_加速', '怪物加血', '血量上限', '冰冻']); + const prefabs = membershipFromArray(['怪_攻击buff', '怪_加速', '怪物加血', '血量上限', '冰冻', '怪_中毒']); for (const fx of ALWAYS_BATTLE_DEBUFF_STATUS_FX) { membershipAdd(prefabs, fx); } diff --git a/assets/script/battle/config/baseAtk/SkillAbilityBuilder.ts b/assets/script/battle/config/baseAtk/SkillAbilityBuilder.ts index 96b095a..e651fa6 100644 --- a/assets/script/battle/config/baseAtk/SkillAbilityBuilder.ts +++ b/assets/script/battle/config/baseAtk/SkillAbilityBuilder.ts @@ -11,6 +11,9 @@ import { normalAbilityId, } from '../weapon1/Weapon1BattleIds'; +/** 冰球术 Skill.id(表现层落地结算) */ +const ICE_BALL_SKILL_ID = 305201; + /** 技能槽位(驱动 cue / Ability id) */ export type SkillAbilityKind = 'normal' | 'hero' | 'weapon' | 'monster'; @@ -695,9 +698,9 @@ export class SkillAbilityBuilder { // 12 散射 / 13 连发 / 17 不穿透散射 / 19 冰锥:伤害由 BowAtkHost 弹道结算,无 deferred 段 // 14 360°多段 / 18 / 20:每段按圆重新索敌结算,无 deferred 段 - // 飞剑主动(atk_type=10 子集):落地结算,无 deferred 段 + // 飞剑主动(atk_type=10 子集)/ 冰球术 305201:表现层落地结算,无 deferred 段 if (atkHead === 12 || atkHead === 13 || atkHead === 14 || atkHead === 17 || atkHead === 18 || atkHead === 19 || atkHead === 20 - || isFlySwordSkillId(row.id)) { + || isFlySwordSkillId(row.id) || row.id === ICE_BALL_SKILL_ID) { return []; } diff --git a/assets/script/battle/presentation/IceBallAtkPresenter.ts b/assets/script/battle/presentation/IceBallAtkPresenter.ts new file mode 100644 index 0000000..aa180a9 --- /dev/null +++ b/assets/script/battle/presentation/IceBallAtkPresenter.ts @@ -0,0 +1,271 @@ +import { isValid, Node, UITransform, Vec3, sp } from 'cc'; +import type { EntityId } from 'db://assets/mx/module/ecs'; +import { TransformComp } from 'db://assets/mx/module/battle/ecs/Components'; +import { GBundle, GPath } from '../../game/ConfigRes'; +import { logicalWorldToCocos } from '../bridge/CocosBattleMovementBridge'; +import type { Skill } from '../skill/Skill'; +import { SkillPresentationEnv } from '../skill/SkillPresentationEnv'; +import type { SkillPresentationContext } from '../skill/SkillPresentationTypes'; +import type { ISkillPresenter } from './SkillPresenter'; +import { BattleSpinePlayback } from './BattleSpinePlayback'; +import { buildShadowDealContext } from './SkillShadowContext'; +import { logicPosOf, pulseHostileCircle } from './ThrownCircleAtkPresenter'; + +/** 冰球术 Skill.id(Battlebuff 3052) */ +export const ICE_BALL_SKILL_ID = 305201; + +/** battleHero/prefab/skill/冰球 */ +const ICE_BALL_PREFAB = '冰球'; +/** 默认动画名 */ +const ICE_BALL_ANIM = 'animation'; +/** Spine「冰球」附件卸下时刻兜底(爆炸) */ +const ICE_BALL_EXPLODE_FALLBACK = 1.1667; +/** 整段动画时长兜底 */ +const ICE_BALL_DURATION_FALLBACK = 2; +/** 影子伤害类型 */ +const ICE_BALL_DMG = 'ice_ball'; + +/** + * 是否冰球术 Skill.id。 + * @param skillId Skill.id + */ +export function isIceBallSkillId(skillId: number): boolean { + return Math.floor(Number(skillId) || 0) === ICE_BALL_SKILL_ID; +} + +/** 一次陨落冰球 */ +interface IceBallRun { + /** 施法者 */ + caster: EntityId; + /** 影子伤害上下文 */ + ctx: SkillPresentationContext; + /** 圆心逻辑 X */ + x: number; + /** 圆心逻辑 Y */ + y: number; + /** 半径像素 */ + radius: number; + /** 伤害倍率 */ + scale: number; + /** 距爆炸剩余秒 */ + explodeWait: number; + /** 爆炸后距回池剩余秒 */ + endWait: number; + /** 是否已结算爆炸伤害 */ + exploded: boolean; + /** 冰球节点 */ + node: Node | null; +} + +let _host: IceBallHost | null = null; + +/** 冰球术表现宿主 */ +export function getIceBallHost(): IceBallHost { + if (!_host) _host = new IceBallHost(); + return _host; +} + +/** + * Skill 305201:释放施法锁后在主目标落点生成「冰球」,动画近结束爆炸并圈伤。 + */ +export class IceBallAtkPresenter implements ISkillPresenter { + present(ctx: SkillPresentationContext, host: Skill): void { + if (!ctx.isAlive) { + ctx.finish(); + return; + } + const seq = host.beginPresentationCast(ctx); + if (!(seq > 0)) return; + host.disarmAnimComplete(); + getIceBallHost().start(ctx); + ctx.releasePresentationLock(); + host.finishPresentationCast(seq, false); + } +} + +/** 冰球陨落:独立计时爆炸圈伤 */ +export class IceBallHost { + private readonly _runs: IceBallRun[] = []; + private _activePulse: IceBallRun | null = null; + + /** + * 开战:锁定落点并生成冰球。 + * @param ctx 表现上下文 + */ + start(ctx: SkillPresentationContext): void { + const parts = String(ctx.params.atkPara ?? '').split(',').map(Number); + const radius = parts[0]; + const wan = parts[1]; + if (!(radius > 0) || !Number.isFinite(wan) || !(wan > 0)) { + console.log('[IceBall] atk_para 无效', ctx.params.skillId, ctx.params.atkPara); + return; + } + const session = SkillPresentationEnv.getBattleSession(); + if (!session) { + console.log('[IceBall] 无战斗会话', ctx.params.skillId); + return; + } + const primary = ctx.targets[0] ?? 0; + const lockId = primary > 0 ? primary : ctx.caster; + const center = logicPosOf(session, lockId) ?? logicPosOf(session, ctx.caster); + if (!center) { + console.log('[IceBall] 无落点', ctx.params.skillId); + return; + } + const placed = this._placeBall(lockId, center); + if (!placed) return; + this._runs.push({ + caster: ctx.caster, + ctx: buildShadowDealContext(ctx, String(ctx.params.buff1 ?? ''), String(ctx.params.buff2 ?? '')), + x: center.x, + y: center.y, + radius, + scale: wan / 10000, + explodeWait: placed.explodeAt, + endWait: Math.max(0.05, placed.duration - placed.explodeAt), + exploded: false, + node: placed.node, + }); + } + + /** + * 推进爆炸与回池。 + * @param dt 逻辑秒 + */ + tick(dt: number): void { + if (!(dt > 0) || this._runs.length <= 0) return; + const session = SkillPresentationEnv.getBattleSession(); + for (let i = this._runs.length - 1; i >= 0; i--) { + const run = this._runs[i]; + if (!session || !session.world.isAlive(run.caster) || !run.ctx.isAlive) { + this._drop(run); + this._runs.splice(i, 1); + continue; + } + if (!run.exploded) { + run.explodeWait -= dt; + if (run.explodeWait <= 0) { + this._explode(session, run); + run.exploded = true; + } + continue; + } + run.endWait -= dt; + if (run.endWait <= 0) { + this._drop(run); + this._runs.splice(i, 1); + } + } + } + + /** 战斗结束清空 */ + abortAll(): void { + for (let i = 0; i < this._runs.length; i++) this._drop(this._runs[i]); + this._runs.length = 0; + } + + private _explode(session: NonNullable>, run: IceBallRun): void { + this._activePulse = run; + pulseHostileCircle(session, run.caster, run.x, run.y, run.radius, this._onPulseHit); + this._activePulse = null; + } + + private readonly _onPulseHit = (id: EntityId): void => { + const run = this._activePulse; + if (!run) return; + run.ctx.dealDamage(id, { scale: run.scale, damageType: ICE_BALL_DMG }); + }; + + /** + * 在目标落点生成冰球并播默认动画。 + * @param lockId 锁定目标 + * @param center 逻辑圆心 + */ + private _placeBall(lockId: EntityId, center: { x: number; y: number }): { node: Node; explodeAt: number; duration: number } | null { + const flyLayer = SkillPresentationEnv.flyLayer; + if (!flyLayer?.isValid) { + console.log('[IceBall] 无飞行层'); + return null; + } + let node: Node | null = null; + try { + node = gg.res.getNode(GPath.HeroSkillPrefab(), ICE_BALL_PREFAB, GBundle.BattleHero); + } catch (e) { + console.log('[IceBall] 预制体加载异常', String(e)); + return null; + } + if (!node?.isValid) { + console.log('[IceBall] 预制体未加载', ICE_BALL_PREFAB); + return null; + } + const ut = flyLayer.getComponent(UITransform); + if (!ut) { + gg.res.putNode(node); + return null; + } + const world = this._resolvePlaceWorld(lockId, center); + if (!world) { + console.log('[IceBall] 无放置世界坐标', lockId); + gg.res.putNode(node); + return null; + } + flyLayer.active = true; + node.parent = flyLayer; + node.active = true; + node.layer = flyLayer.layer; + node.setScale(1, 1, 1); + node.setPosition(ut.convertToNodeSpaceAR(world)); + node.setSiblingIndex(flyLayer.children.length - 1); + + const sk = node.getComponentInChildren(sp.Skeleton); + if (!sk?.isValid) { + console.log('[IceBall] 缺 Spine', ICE_BALL_PREFAB); + gg.res.putNode(node); + return null; + } + const typed = sk as sp.Skeleton & { defaultAnimation?: string }; + const anim = String(typed.defaultAnimation || ICE_BALL_ANIM).trim() || ICE_BALL_ANIM; + if (!BattleSpinePlayback.play(sk, anim, { loop: false })) { + console.log('[IceBall] 动画起播失败', anim); + gg.res.putNode(node); + return null; + } + const durationRaw = BattleSpinePlayback.getAnimDuration(sk, anim); + const duration = durationRaw != null && durationRaw > 0 ? durationRaw : ICE_BALL_DURATION_FALLBACK; + const explodeRaw = BattleSpinePlayback.findFirstAttachmentClearTime(sk, anim, '冰球'); + let explodeAt = explodeRaw != null && explodeRaw > 0 ? explodeRaw : ICE_BALL_EXPLODE_FALLBACK; + if (explodeAt >= duration) explodeAt = Math.max(0.05, duration * 0.85); + return { node, explodeAt, duration }; + } + + /** + * 放置世界坐标:优先怪节点,否则逻辑坐标映射。 + * @param lockId 锁定目标 + * @param center 逻辑圆心 + */ + private _resolvePlaceWorld(lockId: EntityId, center: { x: number; y: number }): Vec3 | null { + const fromMonster = SkillPresentationEnv.getTargetWorldPos(lockId); + if (fromMonster) return fromMonster; + const session = SkillPresentationEnv.getBattleSession(); + const map = SkillPresentationEnv.getMap(); + if (!session || !map) return null; + const tf = session.world.getComponent(lockId, TransformComp); + if (!tf) return null; + return logicalWorldToCocos(session, map, tf.row, tf.col, center.x, center.y); + } + + private _drop(run: IceBallRun): void { + const node = run.node; + run.node = null; + if (!node?.isValid) return; + const skins = node.getComponentsInChildren(sp.Skeleton); + for (let i = 0; i < skins.length; i++) { + BattleSpinePlayback.clearPendingComplete(skins[i]); + BattleSpinePlayback.resetTracks(skins[i]); + } + if (node['resKey']) gg.res.putNode(node); + else if (isValid(node)) node.destroy(); + const flyLayer = SkillPresentationEnv.flyLayer; + if (flyLayer?.isValid && flyLayer.children.length === 0) flyLayer.active = false; + } +} diff --git a/assets/script/battle/presentation/IceBallAtkPresenter.ts.meta b/assets/script/battle/presentation/IceBallAtkPresenter.ts.meta new file mode 100644 index 0000000..22fa085 --- /dev/null +++ b/assets/script/battle/presentation/IceBallAtkPresenter.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "3380c6f6-3d3d-4da3-bfcf-e7273d59bee3", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/script/battle/presentation/SpecialSkillPresenters.ts b/assets/script/battle/presentation/SpecialSkillPresenters.ts index 051932b..9b2c038 100644 --- a/assets/script/battle/presentation/SpecialSkillPresenters.ts +++ b/assets/script/battle/presentation/SpecialSkillPresenters.ts @@ -10,6 +10,7 @@ import { RingProjectilesPresenter } from './RingProjectilesPresenter'; import { SpearTossPresenter } from './SpearTossPresenter'; import { PagodaSmashPresenter } from './PagodaSmashPresenter'; import { FlySwordAtkPresenter } from './FlySwordAtkPresenter'; +import { IceBallAtkPresenter, ICE_BALL_SKILL_ID } from './IceBallAtkPresenter'; import { FLY_SWORD_SKILL_IDS } from '../config/weapon1/Weapon1BattleIds'; import { Gong } from '../heros/Gong'; import { Gun } from '../heros/Gun'; @@ -131,6 +132,7 @@ export function ensureSpecialSkillPresenters(): void { for (let i = 0; i < FLY_SWORD_SKILL_IDS.length; i++) { router.registerSkillId(FLY_SWORD_SKILL_IDS[i], flySword); } + router.registerSkillId(ICE_BALL_SKILL_ID, new IceBallAtkPresenter()); // 覆盖内置 atk_type=16:棍宿主播攻效,其余走通用多段 router.registerAtkType(16, new FanMultiHitAtkPresenter()); // 覆盖内置 atk_type 12/17:弓宿主散射走 Gong 自身投掷物,其余通用 diff --git a/assets/script/battle/runtime/BattleRuntime.ts b/assets/script/battle/runtime/BattleRuntime.ts index a649f9e..77962a0 100644 --- a/assets/script/battle/runtime/BattleRuntime.ts +++ b/assets/script/battle/runtime/BattleRuntime.ts @@ -41,6 +41,7 @@ import { getArrowRainHost } from '../presentation/ArrowRainPresenter'; import { getExtraRingHost } from '../presentation/RingProjectilesPresenter'; import { getExtraStrikeHost } from '../presentation/ExtraStrikePresenter'; import { getGroundCircleHost, getThrownCircleHost } from '../presentation/ThrownCircleAtkPresenter'; +import { getIceBallHost } from '../presentation/IceBallAtkPresenter'; import { getFlySwordHost } from '../presentation/FlySwordAtkPresenter'; import { getSpearTossHost } from '../presentation/SpearTossPresenter'; import { getPagodaSmashHost } from '../presentation/PagodaSmashPresenter'; @@ -992,6 +993,7 @@ export class BattleRuntime implements IBattleRuntimeHandle { getThrownCircleHost().tick(scaledDt); getGroundCircleHost().tick(scaledDt); getFlySwordHost().tick(scaledDt); + getIceBallHost().tick(scaledDt); return; default: return; @@ -1236,6 +1238,7 @@ export class BattleRuntime implements IBattleRuntimeHandle { getThrownCircleHost().abortAll(); getGroundCircleHost().abortAll(); getFlySwordHost().abortAll(); + getIceBallHost().abortAll(); getSpearTossHost().abortAll(); getPagodaSmashHost().abortAll(); getStaffSmashHost().abortAll(); diff --git a/assets/script/battle/view/CocosBattleCombatPresenter.ts b/assets/script/battle/view/CocosBattleCombatPresenter.ts index 38e8536..11092a6 100644 --- a/assets/script/battle/view/CocosBattleCombatPresenter.ts +++ b/assets/script/battle/view/CocosBattleCombatPresenter.ts @@ -24,12 +24,15 @@ import { SWORD_ARRAY_EVENT, SWORD_WAVE_EVENT, LIGHTNING_CHAIN_EVENT, + POISON_APPLIED_EVENT, + POISON_REMOVED_EVENT, type HomingDragonCue, type IceConeSplitCue, type LineDragonCue, type SwordArrayCue, type SwordWaveCue, type LightningChainCue, + type PoisonCue, } from '../combat/PassiveSkillRuntime'; import { logicalWorldToCocos } from '../bridge/CocosBattleMovementBridge'; import { BattleMapSpec } from '../map/BattleMapSpec'; @@ -58,6 +61,8 @@ type MonsterView = IBattleMonsterView & { clearGhostBiteFx?(): void; showElecFx?(durationSec: number): void; clearElecFx?(): void; + showPoisonFx?(durationSec: number): void; + clearPoisonFx?(): void; }; /** 阎王主动「生死簿」Skill.id 区间 */ @@ -229,6 +234,7 @@ export class CocosBattleCombatPresenter { this._onBleedRemoved(event.victim); this._onGhostBiteRemoved(event.victim); this._onElecRemoved(event.victim); + this._onPoisonRemoved(event.victim); break; case BattleEventType.UnitDespawned: this._onStunRemoved(event.entity); @@ -238,6 +244,7 @@ export class CocosBattleCombatPresenter { this._onBleedRemoved(event.entity); this._onGhostBiteRemoved(event.entity); this._onElecRemoved(event.entity); + this._onPoisonRemoved(event.entity); break; case BattleEventType.Custom: if (event.name === KNOCKBACK_APPLIED_EVENT) { @@ -257,6 +264,11 @@ export class CocosBattleCombatPresenter { this._playLightningChain(event.payload as LightningChainCue | undefined); } else if (event.name === ICE_CONE_SPLIT_EVENT) { this._playIceConeSplit(event.payload as IceConeSplitCue | undefined); + } else if (event.name === POISON_APPLIED_EVENT) { + this._onPoisonApplied(event.payload as PoisonCue | undefined); + } else if (event.name === POISON_REMOVED_EVENT) { + const payload = event.payload as { target?: EntityId } | undefined; + if (payload?.target != null) this._onPoisonRemoved(payload.target); } break; } @@ -340,6 +352,19 @@ export class CocosBattleCombatPresenter { this._getMonsterView(target)?.clearElecFx?.(); } + /** 中毒:挂「怪_中毒」并飘「中毒」 */ + private _onPoisonApplied(cue: PoisonCue | undefined): void { + if (!cue || !(cue.target > 0) || !(cue.duration > 0)) return; + const monster = this._getMonsterView(cue.target); + if (!monster) return; + monster.showPoisonFx?.(Math.max(0.05, cue.duration)); + this._playStatusText(cue.target, '中毒'); + } + + private _onPoisonRemoved(target: EntityId): void { + this._getMonsterView(target)?.clearPoisonFx?.(); + } + /** 怪物头顶状态飘字 */ private _playStatusText(target: EntityId, text: string): void { getMonsterStatusText().play(target, text); diff --git a/assets/script/game/GameController.ts b/assets/script/game/GameController.ts index bcf73a5..d99888e 100644 --- a/assets/script/game/GameController.ts +++ b/assets/script/game/GameController.ts @@ -123,6 +123,8 @@ export class GameController extends Component { /** GM:背包刷新仅从该 Weapon2 列表中随机(null=正常权重池) */ gmBagWeapon2Ids: number[] | null = null; + /** GM:本机开局灌入 CarryStore 的 Battlebuff.id(null=不灌;开战 clear 后再写入) */ + gmCarryBuffIds: number[] | null = null; /** GM:AI 主动 Battlebuff.id(null=每局匹配随机;有值则跨局常驻覆盖直至清除) */ gmAiActiveBuffIds: number[] | null = null; /** GM:AI 被动 Battlebuff.id(null=每局匹配随机;有值则跨局常驻覆盖直至清除) */ @@ -409,6 +411,7 @@ export class GameController extends Component { this.isShowObstacle = true this.monsterNumScale = 1; this.gmBagWeapon2Ids = null; + this.gmCarryBuffIds = null; this.gmAiActiveBuffIds = null; this.gmAiPassiveBuffIds = null; this.gmMapId = 0; diff --git a/assets/script/gm/ProjectGmActions.ts b/assets/script/gm/ProjectGmActions.ts index cbe923e..b3a920d 100644 --- a/assets/script/gm/ProjectGmActions.ts +++ b/assets/script/gm/ProjectGmActions.ts @@ -6,12 +6,10 @@ import { TableNames } from '../game/ConfigTableData'; import { UIID } from '../game/ConfigRes'; import { sdkConfig } from '../sdk/sdkConfig'; import { DailyShopDataManager } from '../manager/DailyShopDataManager'; -import { DailyShopKind } from '../manager/DailyShopTypes'; import { EmolumentDataManager } from '../manager/EmolumentDataManager'; import { HeadIconManager } from '../manager/HeadIconManager'; import { RankBoardManager } from '../manager/RankBoardManager'; import { RankDataManager } from '../manager/RankDataManager'; -import { BattleBuffCarryStore } from '../manager/battleBuff/BattleBuffCarryStore'; /** 俸禄 GM 测试快照星级 */ const GM_EMOLUMENT_SNAP_STARS = 50; @@ -191,10 +189,11 @@ export class ProjectGmActions { gg.ui.showToast('已重置每日商店/抽奖(保留携带道具)'); } - /** 清空携带 BUFF */ + /** 清空 GM 词条覆盖 */ static clearCarriedBuffs(): void { + gg.game.gmCarryBuffIds = null; DailyShopDataManager.gmClearCarriedBuffs(); - gg.ui.showToast('已清空携带的每日商店BUFF'); + gg.ui.showToast('已清空 GM 词条'); } /** 清除 AI 主动 BUFF */ @@ -220,24 +219,61 @@ export class ProjectGmActions { /** AI BUFF 摘要 */ static aiBuffSummary(active: boolean): string { const cur = active ? gg.game.gmAiActiveBuffIds : gg.game.gmAiPassiveBuffIds; - return cur?.length ? cur.join(',') : '每局随机'; + if (!cur?.length) return '每局随机'; + const parts: string[] = []; + for (let i = 0; i < cur.length; i++) { + const id = cur[i]; + const row = gg.data.table.getTableData(TableNames.Battlebuff, id); + parts.push(row?.name ? `${id}:${row.name}` : `${id}`); + } + return parts.join(', '); } - /** 写入被动携带第一槽 */ + /** 写入被动携带(追加单个,兼容旧调用) */ static setPassiveCarry(buffId: number): void { - const result = DailyShopDataManager.gmSetFirstCarrySlot(DailyShopKind.Passive, String(buffId)); - if (!result.ok) { - gg.ui.showToast(result.message ?? '写入失败'); - return; + this.setCarryBuffIds([buffId]); + } + + /** + * 覆盖 GM 词条(跨局常驻,开战灌入 CarryStore)。 + * @param ids Battlebuff.id 列表 + * @param quiet 为 true 时不弹 toast(列表即时写入用) + */ + static setCarryBuffIds(ids: number[], quiet = false): void { + const cleaned: number[] = []; + for (let i = 0; i < ids.length; i++) { + const id = Math.floor(Number(ids[i]) || 0); + if (id <= 0) continue; + const row = gg.data.table.getTableData(TableNames.Battlebuff, id); + if (!row) { + console.warn('[GM] 词条 id 不存在', id); + continue; + } + cleaned.push(id); + } + gg.game.gmCarryBuffIds = cleaned.length > 0 ? cleaned : null; + if (!quiet) { + gg.ui.showToast(cleaned.length > 0 ? `已设置 ${cleaned.length} 个词条(开战生效)` : '已清空词条'); } - gg.data.saveToServer(); - gg.ui.showToast(`被动 buff 已写入 ${buffId}`); + } + + /** 当前 GM 词条 id 列表 */ + static getCarryBuffIds(): number[] { + const cur = gg.game.gmCarryBuffIds; + return cur?.length ? cur.slice() : []; } /** 被动携带摘要 */ static passiveCarrySummary(): string { - const first = BattleBuffCarryStore.getPassives(0)[0]; - return first ? `${first.id}(${first.name})` : '空'; + const ids = this.getCarryBuffIds(); + if (!ids.length) return '空'; + const parts: string[] = []; + for (let i = 0; i < ids.length; i++) { + const id = ids[i]; + const row = gg.data.table.getTableData(TableNames.Battlebuff, id); + parts.push(row?.name ? `${id}:${row.name}` : `${id}`); + } + return parts.join(', '); } /** 排行星级&胜场测试 */ diff --git a/assets/script/gm/ProjectGmBootstrap.ts b/assets/script/gm/ProjectGmBootstrap.ts index 1411784..db182d8 100644 --- a/assets/script/gm/ProjectGmBootstrap.ts +++ b/assets/script/gm/ProjectGmBootstrap.ts @@ -59,11 +59,9 @@ export class ProjectGmBootstrap { .sw('敌方营地不掉血', () => A.getEnemyCampImmune(), (v) => A.setCampImmune(false, v)) .page('固定地图', P.levelPicker, () => A.fixedMapSummary()) .num('玩家局内血量', () => A.getWallHp() || 1000, (v) => A.setWallHp(v), 1) - .page('设置被动BUFF', P.buffPicker, () => A.passiveCarrySummary(), { expectType: 2, mode: 'carryPassive' }) - .page('设置AI主动BUFF', P.buffPicker, () => A.aiBuffSummary(true), { expectType: 1, mode: 'aiActive' }) - .page('设置AI被动BUFF', P.buffPicker, () => A.aiBuffSummary(false), { expectType: 2, mode: 'aiPassive' }) - .btn('清除AI主动BUFF', () => A.clearAiActiveBuff()) - .btn('清除AI被动BUFF', () => A.clearAiPassiveBuff()) + + .tab('词条') + .embed(P.buffPicker, { expectType: 0, mode: 'carryPassive' }) .tab('存档') .embed(P.saveEditor) diff --git a/assets/script/gm/ProjectGmPanels.ts b/assets/script/gm/ProjectGmPanels.ts index 83a3bd0..49f9665 100644 --- a/assets/script/gm/ProjectGmPanels.ts +++ b/assets/script/gm/ProjectGmPanels.ts @@ -1,5 +1,5 @@ import { - Button, Color, Label, Layout, Mask, Node, ScrollView, Sprite, UITransform, Widget, + Button, Color, Graphics, Label, Layout, Mask, Node, ScrollView, Sprite, UITransform, Widget, HorizontalTextAlignment, VerticalTextAlignment, Layers, Overflow, } from 'cc'; import { SpriteLoad } from '../../mx/components/SpriteLoad'; @@ -7,6 +7,8 @@ import type { IGmContentPanel, IGmPanelContext } from '../../mx/module/gm'; import { GmRegistry } from '../../mx/module/gm'; import { GBundle, GPath } from '../game/ConfigRes'; import { TableNames } from '../game/ConfigTableData'; +import { battleBuffSlotType } from '../manager/BattleBuffParse'; +import { BattleBuffSlotType } from '../manager/battleBuff/BattleBuffTypes'; import { RankDataManager } from '../manager/RankDataManager'; import { ProjectGmActions } from './ProjectGmActions'; import { GmSaveEditorPanel } from './ProjectGmSavePanel'; @@ -22,14 +24,47 @@ export const ProjectGmPanelId = { /** * buffPicker 面板数据。 + * 新表已无 type 列时 expectType 请传 0,否则列表会被滤空。 */ export interface IGmBuffPickerData { - /** 1=主动 2=被动;0=不限 */ + /** BattleBuffSlotType:1主动 2被动 3自动;0=不限(推荐) */ expectType: number; /** 写入目标 */ mode: 'carryPassive' | 'aiActive' | 'aiPassive'; } +const BUFF_C = { + text: new Color(255, 255, 255, 255), + dim: new Color(180, 190, 210, 255), + accent: new Color(255, 220, 70, 255), + rowBg: new Color(28, 36, 56, 255), + rowLine: new Color(100, 160, 240, 160), + rowOn: new Color(255, 220, 70, 255), + btn: new Color(45, 105, 190, 255), + btnClear: new Color(210, 120, 35, 255), + btnBack: new Color(70, 78, 96, 255), +}; + +const QUALITY_COLOR: { [q: number]: Color } = { + 1: new Color(90, 210, 120, 255), + 2: new Color(80, 160, 255, 255), + 3: new Color(190, 110, 255, 255), + 4: new Color(255, 170, 60, 255), +}; + +const QUALITY_NAME: { [q: number]: string } = { + 1: '绿', + 2: '蓝', + 3: '紫', + 4: '橙', +}; + +const SLOT_NAME: { [t: number]: string } = { + [BattleBuffSlotType.Active]: '主动', + [BattleBuffSlotType.Passive]: '被动', + [BattleBuffSlotType.Auto]: '自动', +}; + /** * 创建文字按钮。 */ @@ -220,73 +255,315 @@ class GmItemPickerPanel implements IGmContentPanel { } /** - * BUFF 列表点选。 + * 词条页:上=未选、下=已选;点击即对调并立刻写入。 */ class GmBuffPickerPanel implements IGmContentPanel { private _root: Node | null = null; + private _ctx: IGmPanelContext | null = null; + private _data: IGmBuffPickerData | null = null; + private _listW = 640; + private _allRows: ITableBattlebuff[] = []; + private _selected: number[] = []; + private _availContent: Node | null = null; + private _selContent: Node | null = null; + private _lbAvail: Label | null = null; + private _lbSel: Label | null = null; open(parent: Node, ctx: IGmPanelContext): void { this.close(); - const data = ctx.command.panelData as IGmBuffPickerData; - if (!data?.mode) { - console.warn('[GM] buffPicker 缺少 panelData'); - ctx.host.toast('面板配置错误'); - ctx.goBack(); - return; - } + this._ctx = ctx; + const raw = ctx.command.panelData as IGmBuffPickerData | undefined; + const data: IGmBuffPickerData = { + expectType: raw?.expectType ?? 0, + mode: raw?.mode ?? 'carryPassive', + }; + this._data = data; + this._selected = this._loadCurrentIds(); + + const pUt = parent.getComponent(UITransform); + const W = pUt?.width || 660; + const H = pUt?.height || 900; + this._listW = W - 16; this._root = new Node('buffPicker'); this._root.layer = Layers.Enum.UI_2D; - this._root.addComponent(UITransform).setContentSize(660, 900); + this._root.addComponent(UITransform).setContentSize(W, H); + const rw = this._root.addComponent(Widget); + rw.isAlignLeft = rw.isAlignRight = rw.isAlignTop = rw.isAlignBottom = true; + rw.left = rw.right = rw.top = rw.bottom = 0; parent.addChild(this._root); - const title = new Node('t'); - title.layer = Layers.Enum.UI_2D; - title.addComponent(UITransform).setContentSize(600, 36); - const tl = title.addComponent(Label); - tl.string = ctx.command.title; - tl.fontSize = 26; - tl.color = Color.WHITE; - tl.horizontalAlign = HorizontalTextAlignment.CENTER; - title.setPosition(0, 420); - this._root.addChild(title); + const headH = 36; + const gap = 10; + const clearH = 44; + const usable = Math.max(200, H - headH * 2 - gap - clearH - 8); + const availH = Math.floor(usable * 0.58); + const selH = usable - availH; + + this._lbAvail = this._addHead(this._root, '未选择(点选加入)', this._listW, headH); + this._lbAvail.node.setPosition(0, H / 2 - headH / 2 - 2); + + const { host: availHost, content: availContent } = makeVScroll(this._root, this._listW, availH); + this._availContent = availContent; + const availLay = availContent.getComponent(Layout); + if (availLay) { + availLay.spacingY = 8; + availLay.paddingBottom = 12; + } + availHost.setPosition(0, H / 2 - headH - availH / 2 - 4); + + const selTopY = -H / 2 + clearH + selH + headH + 4; + this._lbSel = this._addHead(this._root, '已选择', this._listW, headH); + this._lbSel.node.setPosition(0, selTopY - headH / 2); + + const { host: selHost, content: selContent } = makeVScroll(this._root, this._listW, selH); + this._selContent = selContent; + const selLay = selContent.getComponent(Layout); + if (selLay) { + selLay.spacingY = 8; + selLay.paddingBottom = 12; + } + selHost.setPosition(0, -H / 2 + clearH + selH / 2); - const { host, content } = makeVScroll(this._root, 640, 700); - host.setPosition(0, 20); + this._pillBtn(this._root, '清空已选', 200, 40, BUFF_C.btnClear, () => this._clearSelected()) + .setPosition(0, -H / 2 + clearH / 2); + this._allRows = []; const list = gg.data.table.getTableList(TableNames.Battlebuff) ?? []; list.sort((a, b) => Number(a.id) - Number(b.id)); for (let i = 0; i < list.length; i++) { const row = list[i]; - const t = Math.floor(Number((row as { type?: number }).type) || 0); - if (data.expectType > 0 && t !== data.expectType) continue; - const id = Number(row.id); - const label = `${id} ${row.name ?? ''} (t${t})`; - makeLabelBtn(content, label, 620, 44, () => { - if (data.mode === 'carryPassive') ProjectGmActions.setPassiveCarry(id); - else if (data.mode === 'aiActive') ProjectGmActions.setAiBuffIds([id], true); - else ProjectGmActions.setAiBuffIds([id], false); - ctx.refreshList(); - ctx.goBack(); - }); + const id = Math.floor(Number(row.id) || 0); + if (id <= 0) continue; + const slot = battleBuffSlotType(row); + if (data.expectType > 0 && slot !== data.expectType) continue; + this._allRows.push(row); } - makeLabelBtn(content, '清除覆盖/空', 620, 44, () => { - if (data.mode === 'carryPassive') { - ctx.host.toast('被动携带请选具体 id'); - return; - } - ProjectGmActions.setAiBuffIds(null, data.mode === 'aiActive'); - ctx.refreshList(); - ctx.goBack(); - }); - content.getComponent(Layout)?.updateLayout(); - - makeLabelBtn(this._root, '返回', 140, 44, () => ctx.goBack()).setPosition(0, -400); + if (this._allRows.length <= 0) { + console.warn('[GM] buffPicker 列表为空', data); + } + this._rebuildLists(); } close(): void { if (this._root?.isValid) this._root.destroy(); this._root = null; + this._ctx = null; + this._data = null; + this._allRows = []; + this._selected = []; + this._availContent = null; + this._selContent = null; + this._lbAvail = null; + this._lbSel = null; + } + + /** 读取当前已生效 id */ + private _loadCurrentIds(): number[] { + if (!this._data) return []; + if (this._data.mode === 'carryPassive') return ProjectGmActions.getCarryBuffIds(); + const cur = this._data.mode === 'aiActive' + ? gg.game.gmAiActiveBuffIds + : gg.game.gmAiPassiveBuffIds; + return cur?.length ? cur.slice() : []; + } + + private _hasId(id: number): boolean { + for (let i = 0; i < this._selected.length; i++) { + if (this._selected[i] === id) return true; + } + return false; + } + + /** 点击未选 → 加入;点击已选 → 移除;立刻写入 */ + private _move(id: number, toSelected: boolean): void { + if (toSelected) { + if (!this._hasId(id)) this._selected.push(id); + } else { + const next: number[] = []; + for (let i = 0; i < this._selected.length; i++) { + if (this._selected[i] !== id) next.push(this._selected[i]); + } + this._selected = next; + } + this._applyNow(); + this._rebuildLists(); + } + + private _clearSelected(): void { + this._selected = []; + this._applyNow(); + this._rebuildLists(); + } + + private _applyNow(): void { + if (!this._data) return; + const ids = this._selected.slice(); + if (this._data.mode === 'carryPassive') { + ProjectGmActions.setCarryBuffIds(ids, true); + } else if (this._data.mode === 'aiActive') { + ProjectGmActions.setAiBuffIds(ids.length ? ids : null, true); + } else { + ProjectGmActions.setAiBuffIds(ids.length ? ids : null, false); + } + } + + private _rebuildLists(): void { + if (!this._availContent?.isValid || !this._selContent?.isValid) return; + this._availContent.removeAllChildren(); + this._selContent.removeAllChildren(); + + let availN = 0; + let selN = 0; + for (let i = 0; i < this._allRows.length; i++) { + const row = this._allRows[i]; + const id = Math.floor(Number(row.id) || 0); + if (this._hasId(id)) { + this._addBuffCard(this._selContent, this._listW, row, false); + selN++; + } else { + this._addBuffCard(this._availContent, this._listW, row, true); + availN++; + } + } + if (availN <= 0) this._addEmptyHint(this._availContent, '已全部选中'); + if (selN <= 0) this._addEmptyHint(this._selContent, '点击上方加入'); + + if (this._lbAvail) this._lbAvail.string = `未选择 ${availN}(点选加入)`; + if (this._lbSel) this._lbSel.string = `已选择 ${selN}(点选移除)`; + + this._availContent.getComponent(Layout)?.updateLayout(); + this._selContent.getComponent(Layout)?.updateLayout(); + } + + private _addEmptyHint(parent: Node, text: string): void { + const n = new Node('empty'); + n.layer = Layers.Enum.UI_2D; + n.addComponent(UITransform).setContentSize(this._listW, 48); + const lb = n.addComponent(Label); + lb.string = text; + lb.fontSize = 22; + lb.color = BUFF_C.dim; + lb.horizontalAlign = HorizontalTextAlignment.CENTER; + parent.addChild(n); + } + + private _addHead(parent: Node, text: string, w: number, h: number): Label { + const n = new Node('head'); + n.layer = Layers.Enum.UI_2D; + n.addComponent(UITransform).setContentSize(w, h); + const lb = n.addComponent(Label); + lb.string = text; + lb.fontSize = 24; + lb.isBold = true; + lb.color = BUFF_C.accent; + lb.horizontalAlign = HorizontalTextAlignment.LEFT; + lb.verticalAlign = VerticalTextAlignment.CENTER; + lb.overflow = Overflow.CLAMP; + parent.addChild(n); + return lb; + } + + /** + * @param toSelected true=点后加入已选;false=点后移回未选 + */ + private _addBuffCard(parent: Node, w: number, row: ITableBattlebuff, toSelected: boolean): void { + const h = 84; + const id = Math.floor(Number(row.id) || 0); + const name = String(row.name ?? ''); + const desc = String(row.desc ?? ''); + const q = Math.max(0, Math.floor(Number(row.quality) || 0)); + const slot = battleBuffSlotType(row); + const qColor = QUALITY_COLOR[q] || BUFF_C.dim; + const qName = QUALITY_NAME[q] || `Q${q}`; + const slotName = slot != null ? (SLOT_NAME[slot] || `t${slot}`) : '?'; + + const card = new Node(`buff${id}`); + card.layer = Layers.Enum.UI_2D; + card.addComponent(UITransform).setContentSize(w, h); + this._drawRound(card, BUFF_C.rowBg, w, h, 12, toSelected ? BUFF_C.rowLine : BUFF_C.rowOn); + + const iconSz = 56; + const iconN = new Node('icon'); + iconN.layer = Layers.Enum.UI_2D; + iconN.addComponent(UITransform).setContentSize(iconSz, iconSz); + const sp = iconN.addComponent(Sprite); + sp.sizeMode = Sprite.SizeMode.CUSTOM; + const loader = iconN.addComponent(SpriteLoad); + const iconName = String(row.icon || row.name || '').trim(); + if (iconName) loader.setSprite(GPath.ShopSkill(iconName), GBundle.Items); + iconN.setPosition(-w / 2 + iconSz / 2 + 10, 0); + card.addChild(iconN); + + const textW = w - iconSz - 28; + const head = new Node('head'); + head.layer = Layers.Enum.UI_2D; + head.addComponent(UITransform).setContentSize(textW, 30); + const hl = head.addComponent(Label); + hl.string = `${id} ${name} · ${qName}/${slotName}`; + hl.fontSize = 22; + hl.isBold = true; + hl.color = qColor; + hl.horizontalAlign = HorizontalTextAlignment.LEFT; + hl.verticalAlign = VerticalTextAlignment.CENTER; + hl.overflow = Overflow.CLAMP; + hl.enableWrapText = false; + head.setPosition(iconSz / 2 + 4, 16); + card.addChild(head); + + const body = new Node('desc'); + body.layer = Layers.Enum.UI_2D; + body.addComponent(UITransform).setContentSize(textW, 34); + const bl = body.addComponent(Label); + bl.string = desc || '(无描述)'; + bl.fontSize = 17; + bl.color = BUFF_C.dim; + bl.horizontalAlign = HorizontalTextAlignment.LEFT; + bl.verticalAlign = VerticalTextAlignment.TOP; + bl.overflow = Overflow.CLAMP; + bl.enableWrapText = true; + body.setPosition(iconSz / 2 + 4, -16); + card.addChild(body); + + card.addComponent(Button); + card.on(Button.EventType.CLICK, () => this._move(id, toSelected), this); + parent.addChild(card); + } + + private _pillBtn(parent: Node, text: string, w: number, h: number, bg: Color, onClick: () => void): Node { + const n = new Node(text); + n.layer = Layers.Enum.UI_2D; + n.addComponent(UITransform).setContentSize(w, h); + this._drawRound(n, bg, w, h, 10); + const lbN = new Node('lb'); + lbN.layer = Layers.Enum.UI_2D; + lbN.addComponent(UITransform).setContentSize(w - 6, h - 4); + const lb = lbN.addComponent(Label); + lb.string = text; + lb.fontSize = 22; + lb.isBold = true; + lb.color = BUFF_C.text; + lb.horizontalAlign = HorizontalTextAlignment.CENTER; + lb.verticalAlign = VerticalTextAlignment.CENTER; + n.addChild(lbN); + n.addComponent(Button); + n.on(Button.EventType.CLICK, onClick, this); + parent.addChild(n); + return n; + } + + private _drawRound(node: Node, fill: Color, w: number, h: number, r: number, stroke?: Color): void { + const g = node.getComponent(Graphics) || node.addComponent(Graphics); + g.clear(); + g.fillColor = fill; + g.roundRect(-w / 2, -h / 2, w, h, r); + g.fill(); + if (stroke) { + g.strokeColor = stroke; + g.lineWidth = stroke === BUFF_C.rowOn ? 3 : 2; + g.roundRect(-w / 2, -h / 2, w, h, r); + g.stroke(); + } } } diff --git a/assets/script/manager/DailyShopDataManager.ts b/assets/script/manager/DailyShopDataManager.ts index ddac51c..60bb53e 100644 --- a/assets/script/manager/DailyShopDataManager.ts +++ b/assets/script/manager/DailyShopDataManager.ts @@ -2,6 +2,7 @@ * 局外每日商店已下线:保留空壳 API,避免旧 UI/GM 引用编译失败。 * 局内 buff 请用 BattleBuffCarryStore / BattleBuffSelectService。 */ +import { BattleBuffCarryStore } from './battleBuff/BattleBuffCarryStore'; import type { DailyShopParsed } from './DailyShopTypes'; import { DAILY_SHOP_ACTIVE_SLOTS, @@ -133,22 +134,40 @@ export class DailyShopDataManager { } static gmClearCarriedBuffs(): void { - // 延迟引用,避免与 CarryStore 启动顺序纠缠 - const { BattleBuffCarryStore } = require('./battleBuff/BattleBuffCarryStore') as typeof import('./battleBuff/BattleBuffCarryStore'); BattleBuffCarryStore.clear(); } /** - * GM:向本机 CarryStore 追加 buff。 + * GM:向本机 CarryStore 追加单个 buff。 * @param _kind 忽略(一律 append) * @param rawId Battlebuff.id */ static gmSetFirstCarrySlot(_kind: DailyShopKind, rawId: string): { ok: boolean; message?: string } { const id = Math.floor(Number(rawId) || 0); if (id <= 0) return { ok: false, message: '非法 id' }; - const { BattleBuffCarryStore } = require('./battleBuff/BattleBuffCarryStore') as typeof import('./battleBuff/BattleBuffCarryStore'); const parsed = BattleBuffCarryStore.appendBuffId(0, id); if (!parsed) return { ok: false, message: '写入失败' }; return { ok: true }; } + + /** + * GM:覆盖本机携带为指定 id 列表(可多个)。 + * @param ids Battlebuff.id 列表 + */ + static gmSetCarryBuffIds(ids: readonly number[]): { ok: boolean; message?: string; count: number } { + BattleBuffCarryStore.setFromBuffIds(0, [], []); + let count = 0; + for (let i = 0; i < ids.length; i++) { + const id = Math.floor(Number(ids[i]) || 0); + if (id <= 0) continue; + const parsed = BattleBuffCarryStore.appendBuffId(0, id); + if (!parsed) { + console.warn('[DailyShop] GM 写入 buff 失败', id); + continue; + } + count++; + } + if (ids.length > 0 && count <= 0) return { ok: false, message: '全部写入失败', count: 0 }; + return { ok: true, count }; + } } diff --git a/assets/script/ui/BattleGame/Monster.ts b/assets/script/ui/BattleGame/Monster.ts index 4f6d250..eef5dba 100644 --- a/assets/script/ui/BattleGame/Monster.ts +++ b/assets/script/ui/BattleGame/Monster.ts @@ -100,6 +100,9 @@ export class Monster extends Component { /** 感电特效节点(怪_电) */ private _elecFx: Node | null = null; private _elecFxSeq = 0; + /** 中毒特效节点(怪_中毒) */ + private _poisonFx: Node | null = null; + private _poisonFxSeq = 0; /** 加速特效节点(怪_加速) */ private _accelerateFx: Node | null = null; /** 加速特效朝向用:上一帧世界坐标 */ @@ -1265,6 +1268,62 @@ export class Monster extends Component { this.clearElecFx(); }; + /** + * 显示中毒特效(prefab/怪_中毒),挂在 bloodPos(头顶),按时长自动回收;刷新则重置计时。 + */ + showPoisonFx(durationSec: number): void { + if (!this.node?.isValid || this.IsDead) return; + const parent = this.bloodPos?.isValid ? this.bloodPos : (this.upEff?.isValid ? this.upEff : this.node); + ++this._poisonFxSeq; + + if (!this._poisonFx?.isValid) { + const node = gg.res.getNode('prefab/', '怪_中毒', GBundle.BattleGameMonster); + if (!node?.isValid) { + console.error('[Monster] 中毒特效预制体未加载', '怪_中毒'); + return; + } + node.parent = parent; + node.setPosition(0, this._poisonFxLocalY(parent), 0); + node.active = true; + this._poisonFx = node; + const sk = node.getComponentInChildren(sp.Skeleton); + if (sk) { + sk.setAnimationCacheMode(sp.SpineAnimationCacheMode.SHARED_CACHE); + sk.setAnimation(0, 'animation', true); + } + } else { + if (this._poisonFx.parent !== parent) this._poisonFx.parent = parent; + this._poisonFx.setPosition(0, this._poisonFxLocalY(parent), 0); + } + + const dur = Math.max(0.05, durationSec); + this.unschedule(this._onPoisonFxTimeout); + this.scheduleOnce(this._onPoisonFxTimeout, dur); + } + + /** + * 中毒特效本地 Y:挂 bloodPos 时为 0;否则对齐 bloodPos 头顶高度。 + * @param parent 挂载父节点 + */ + private _poisonFxLocalY(parent: Node): number { + if (parent === this.bloodPos) return 0; + if (this.bloodPos?.isValid) return this.bloodPos.position.y; + return 0; + } + + clearPoisonFx(): void { + this._poisonFxSeq++; + this.unschedule(this._onPoisonFxTimeout); + if (this._poisonFx?.isValid) { + gg.res.putNode(this._poisonFx); + } + this._poisonFx = null; + } + + private _onPoisonFxTimeout = (): void => { + this.clearPoisonFx(); + }; + /** 按 Weapon1.atkPos 取受击点世界坐标(1头 2腰 3脚) */ getPosByAtkPosIndex(atkPosIndex: number): Vec3 { const ui = this.node.getComponent(UITransform); @@ -1388,6 +1447,7 @@ export class Monster extends Component { this.clearBleedFx(); this.clearGhostBiteFx(); this.clearElecFx(); + this.clearPoisonFx(); this.clearAccelerateFx(); this.clearAddHpEff(); this.clearHpCapEff();