宝石镶嵌和展示功能

master
humingxing 1 week ago
parent 1388366f39
commit e9780e6f1e
  1. 124
      assets/bundles/battleHero/prefab/weapon/武器.prefab
  2. 312
      assets/bundles/first/config/Battlebuff.json
  3. 1427
      assets/bundles/gemstone/prefab/PanelGemstone.prefab
  4. 1896
      assets/bundles/popBattle/battleGameResult/prefab/BattleWeaponDescHero.prefab
  5. 1
      assets/bundles/popCommon/popGemInfo.meta
  6. 1
      assets/bundles/popCommon/popGemInfo/prefab.meta
  7. 1669
      assets/bundles/popCommon/popGemInfo/prefab/PopGemInfo.prefab
  8. 13
      assets/bundles/popCommon/popGemInfo/prefab/PopGemInfo.prefab.meta
  9. 21
      assets/bundles/popCommon/popGetGem/prefab/PopGetGem.prefab
  10. 3
      assets/mx/module/redDot/RedDotComp.ts
  11. BIN
      assets/res/comm/texture/bg/英雄信息框.png
  12. 56
      assets/res/comm/texture/bg/英雄信息框.png.meta
  13. 2
      assets/res/font/str.txt
  14. BIN
      assets/res/gemstone/texture/武器信息图标底框--绿.png
  15. 134
      assets/res/gemstone/texture/武器信息图标底框--绿.png.meta
  16. BIN
      assets/res/gemstone/texture/武器信息图标底框-橙.png
  17. 134
      assets/res/gemstone/texture/武器信息图标底框-橙.png.meta
  18. BIN
      assets/res/gemstone/texture/武器信息图标底框-紫.png
  19. 134
      assets/res/gemstone/texture/武器信息图标底框-紫.png.meta
  20. BIN
      assets/res/gemstone/texture/武器信息图标底框-红.png
  21. 134
      assets/res/gemstone/texture/武器信息图标底框-红.png.meta
  22. BIN
      assets/res/gemstone/texture/武器信息图标底框-蓝.png
  23. 134
      assets/res/gemstone/texture/武器信息图标底框-蓝.png.meta
  24. BIN
      assets/res/gemstone/texture/矩形 798 拷贝 2-1.png
  25. BIN
      assets/res/gemstone/texture/矩形 798 拷贝 2.png
  26. 134
      assets/res/gemstone/texture/矩形 798 拷贝 2.png.meta
  27. BIN
      assets/res/gemstone/texture/矩形 798 拷贝 3-1.png
  28. 134
      assets/res/gemstone/texture/矩形 798 拷贝 3-1.png.meta
  29. BIN
      assets/res/gemstone/texture/矩形 798 拷贝 3.png
  30. 134
      assets/res/gemstone/texture/矩形 798 拷贝 3.png.meta
  31. BIN
      assets/res/gemstone/texture/矩形 798 拷贝-1.png
  32. 134
      assets/res/gemstone/texture/矩形 798 拷贝-1.png.meta
  33. BIN
      assets/res/gemstone/texture/矩形 798 拷贝.png
  34. 134
      assets/res/gemstone/texture/矩形 798 拷贝.png.meta
  35. 160
      assets/script/battle/combat/GemBattleEffect.ts
  36. 9
      assets/script/battle/combat/GemBattleEffect.ts.meta
  37. 37
      assets/script/battle/combat/TowerLoadoutService.ts
  38. 2
      assets/script/battle/combat/TowerQueryTypes.ts
  39. 2
      assets/script/battle/combat/TowerStatResolver.ts
  40. 7
      assets/script/battle/view/CocosBagBoardView.ts
  41. 97
      assets/script/com/GemGrid.ts
  42. 4
      assets/script/game/ConfigRes.ts
  43. 11
      assets/script/game/GM.ts
  44. 37
      assets/script/manager/GemDataManager.ts
  45. 20
      assets/script/ui/BattleWeaponDescHero/Auto_BattleWeaponDescHero.ts
  46. 657
      assets/script/ui/BattleWeaponDescHero/UIBattleWeaponDescHero.ts
  47. 7
      assets/script/ui/PanelGemstone/Auto_PanelGemstone.ts
  48. 196
      assets/script/ui/PanelGemstone/UIPanelGemstone.ts
  49. 9
      assets/script/ui/PopGemInfo.meta
  50. 49
      assets/script/ui/PopGemInfo/Auto_PopGemInfo.ts
  51. 9
      assets/script/ui/PopGemInfo/Auto_PopGemInfo.ts.meta
  52. 90
      assets/script/ui/PopGemInfo/UIPopGemInfo.ts
  53. 9
      assets/script/ui/PopGemInfo/UIPopGemInfo.ts.meta
  54. 30
      docs/需求-宝石装备合成.md
  55. BIN
      配置表/excelList/~$关卡表.xlsx
  56. BIN
      配置表/excelList/~$武器表.xlsx
  57. BIN
      配置表/excelList/~$随机buff.xlsx
  58. BIN
      配置表/策划配置/~$数字2048资源.xlsx

@ -25,20 +25,17 @@
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 16 "__id__": 14
},
{
"__id__": 18
}, },
{ {
"__id__": 20 "__id__": 16
}, },
{ {
"__id__": 22 "__id__": 18
} }
], ],
"_prefab": { "_prefab": {
"__id__": 24 "__id__": 20
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@ -85,14 +82,14 @@
"_active": true, "_active": true,
"_components": [ "_components": [
{ {
"__id__": 11 "__id__": 9
}, },
{ {
"__id__": 13 "__id__": 11
} }
], ],
"_prefab": { "_prefab": {
"__id__": 15 "__id__": 13
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
@ -109,8 +106,8 @@
}, },
"_lscale": { "_lscale": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 1, "x": 0.5,
"y": 1, "y": 0.5,
"z": 1 "z": 1
}, },
"_mobility": 0, "_mobility": 0,
@ -139,18 +136,15 @@
}, },
{ {
"__id__": 6 "__id__": 6
},
{
"__id__": 8
} }
], ],
"_prefab": { "_prefab": {
"__id__": 10 "__id__": 8
}, },
"_lpos": { "_lpos": {
"__type__": "cc.Vec3", "__type__": "cc.Vec3",
"x": 35.092, "x": 75.092,
"y": 14.945, "y": 76.945,
"z": 0 "z": 0
}, },
"_lrot": { "_lrot": {
@ -249,42 +243,6 @@
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
"fileId": "a1SideMarkSp01" "fileId": "a1SideMarkSp01"
}, },
{
"__type__": "cc.Widget",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 3
},
"_enabled": true,
"__prefab": {
"__id__": 9
},
"_alignFlags": 33,
"_target": null,
"_left": 0,
"_right": -19.092,
"_top": -6.9449999999999985,
"_bottom": 0,
"_horizontalCenter": 0,
"_verticalCenter": 0,
"_isAbsLeft": true,
"_isAbsRight": true,
"_isAbsTop": true,
"_isAbsBottom": true,
"_isAbsHorizontalCenter": true,
"_isAbsVerticalCenter": true,
"_originalWidth": 0,
"_originalHeight": 0,
"_alignMode": 1,
"_lockFlags": 0,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "13cuE53/9Oa7G3WvpiiwQJ"
},
{ {
"__type__": "cc.PrefabInfo", "__type__": "cc.PrefabInfo",
"root": { "root": {
@ -308,12 +266,12 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 12 "__id__": 10
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
"width": 68, "width": 148,
"height": 35 "height": 159
}, },
"_anchorPoint": { "_anchorPoint": {
"__type__": "cc.Vec2", "__type__": "cc.Vec2",
@ -336,7 +294,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 14 "__id__": 12
}, },
"_customMaterial": null, "_customMaterial": null,
"_srcBlendFactor": 2, "_srcBlendFactor": 2,
@ -391,12 +349,12 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 17 "__id__": 15
}, },
"_contentSize": { "_contentSize": {
"__type__": "cc.Size", "__type__": "cc.Size",
"width": 88, "width": 101,
"height": 48 "height": 101
}, },
"_anchorPoint": { "_anchorPoint": {
"__type__": "cc.Vec2", "__type__": "cc.Vec2",
@ -419,7 +377,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 19 "__id__": 17
}, },
"_customMaterial": null, "_customMaterial": null,
"_srcBlendFactor": 2, "_srcBlendFactor": 2,
@ -434,7 +392,7 @@
"_spriteFrame": null, "_spriteFrame": null,
"_type": 0, "_type": 0,
"_fillType": 0, "_fillType": 0,
"_sizeMode": 0, "_sizeMode": 1,
"_fillCenter": { "_fillCenter": {
"__type__": "cc.Vec2", "__type__": "cc.Vec2",
"x": 0, "x": 0,
@ -461,7 +419,7 @@
}, },
"_enabled": true, "_enabled": true,
"__prefab": { "__prefab": {
"__id__": 21 "__id__": 19
}, },
"bundleName": "icons", "bundleName": "icons",
"SpriteNode": null, "SpriteNode": null,
@ -472,44 +430,6 @@
"__type__": "cc.CompPrefabInfo", "__type__": "cc.CompPrefabInfo",
"fileId": "fdG72hvtxL5KIJS0cLCD+q" "fileId": "fdG72hvtxL5KIJS0cLCD+q"
}, },
{
"__type__": "cc.Layout",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 23
},
"_resizeMode": 1,
"_layoutType": 1,
"_cellSize": {
"__type__": "cc.Size",
"width": 40,
"height": 40
},
"_startAxis": 0,
"_paddingLeft": 10,
"_paddingRight": 10,
"_paddingTop": 0,
"_paddingBottom": 0,
"_spacingX": 0,
"_spacingY": 0,
"_verticalDirection": 1,
"_horizontalDirection": 0,
"_constraint": 0,
"_constraintNum": 2,
"_affectedByScale": false,
"_isAlign": false,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "ddC68KlNdDXb6cUApjvONA"
},
{ {
"__type__": "cc.PrefabInfo", "__type__": "cc.PrefabInfo",
"root": { "root": {

@ -1,117 +1,117 @@
[ [
{ {
"id": 1, "id": 1,
"name": "人参果", "name": "灾厄盒",
"desc": "获得后体力+10", "desc": "对方Boss出现几率翻倍",
"icon": "人参果", "icon": "灾厄盒",
"quality": 1, "quality": 2,
"type": 3, "type": 2,
"functiontype": 1, "functiontype": 1,
"functionpar": 10, "functionpar": 10000,
"weight": 1, "weight": 1,
"price": 40 "price": 90
}, },
{ {
"id": 2, "id": 2,
"name": "封神榜", "name": "倍率卡",
"desc": "拖到我方单位后,升一级。", "desc": "一定几率召唤出倍率卡,拖到数字上升1级或2级",
"icon": "封神榜", "icon": "倍率卡",
"quality": 4, "quality": 4,
"type": 1, "type": 1,
"functiontype": 2, "functiontype": 2,
"functionpar": "65,1", "functionpar": "1000,80,20",
"weight": 1, "weight": 1,
"price": 150 "price": 150
}, },
{ {
"id": 3, "id": 3,
"name": "蟠桃树", "name": "攻速符",
"desc": "每20秒产生1个蟠桃,升级翻倍。", "desc": "我方所有数字攻速+40%",
"icon": "蟠桃树", "icon": "攻速符",
"quality": 3, "quality": 3,
"type": 2, "type": 2,
"functiontype": 3, "functiontype": 3,
"functionpar": "30,102", "functionpar": 4000,
"weight": 1, "weight": 1,
"price": 120 "price": 120
}, },
{ {
"id": 4, "id": 4,
"name": "缚仙网", "name": "赏金令",
"desc": "拖到道路上,敌人接触后无法挣脱", "desc": "击杀怪物后,20%概率多获得一个金币",
"icon": "缚仙网", "icon": "赏金令",
"quality": 1, "quality": 2,
"type": 1, "type": 2,
"functiontype": 4, "functiontype": 4,
"functionpar": "60,5", "functionpar": 2000,
"weight": 1, "weight": 1,
"price": 40 "price": 90
}, },
{ {
"id": 5, "id": 5,
"name": "乾坤袋", "name": "倍增卡",
"desc": "拖到要回收的字上,回收换成蟠桃", "desc": "召唤出的数字,一定几率*2",
"icon": "乾坤袋", "icon": "倍增卡",
"quality": 4, "quality": 2,
"type": 1, "type": 2,
"functiontype": 5, "functiontype": 5,
"functionpar": "0,1", "functionpar": 1000,
"weight": 1, "weight": 1,
"price": 150 "price": 90
}, },
{ {
"id": 6, "id": 6,
"name": "镇宅牌", "name": "幸运石",
"desc": "我方出兵点血量+3", "desc": "宝石掉落几率+50%",
"icon": "镇宅牌", "icon": "幸运石",
"quality": 1, "quality": 3,
"type": 2, "type": 2,
"functiontype": 6, "functiontype": 6,
"functionpar": "3", "functionpar": 5000,
"weight": 1, "weight": 1,
"price": 40 "price": 120
}, },
{ {
"id": 7, "id": 7,
"name": "授仙箓", "name": "鏖战册",
"desc": "拖到我方单位后,有概率升1级或降1级", "desc": "我方攻速+50%,对方攻速+30%",
"icon": "授仙箓", "icon": "鏖战册",
"quality": 3, "quality": 3,
"type": 1, "type": 2,
"functiontype": 7, "functiontype": 7,
"functionpar": "60,10000,9000,7500,6500", "functionpar": 50003000,
"weight": 1, "weight": 1,
"price": 120 "price": 120
}, },
{ {
"id": 8, "id": 8,
"name": "召神令", "name": "气血丸",
"desc": "召唤出神将的概率提升2倍", "desc": "我方π的血量+3",
"icon": "召神令", "icon": "气血丸",
"quality": 3, "quality": 2,
"type": 2, "type": 2,
"functiontype": 8, "functiontype": 8,
"functionpar": "10", "functionpar": 3,
"weight": 1, "weight": 1,
"price": 120 "price": 90
}, },
{ {
"id": 9, "id": 9,
"name": "化龙铲", "name": "双生盾",
"desc": "每60秒自动产生一把铲子", "desc": "我方π血量+5,对方+3",
"icon": "化龙铲", "icon": "双生盾",
"quality": 4, "quality": 3,
"type": 2, "type": 2,
"functiontype": 9, "functiontype": 9,
"functionpar": "60", "functionpar": "5,3",
"weight": 1, "weight": 1,
"price": 150 "price": 120
}, },
{ {
"id": 10, "id": 10,
"name": "掘宝仙铲", "name": "寻金尺",
"desc": "所有铲子变成掘宝铲,可以挖出蟠桃", "desc": "解锁地块时额外产生金币",
"icon": "掘宝仙铲", "icon": "寻金尺",
"quality": 4, "quality": 4,
"type": 2, "type": 2,
"functiontype": 10, "functiontype": 10,
@ -121,206 +121,74 @@
}, },
{ {
"id": 11, "id": 11,
"name": "文昌笔", "name": "御雷珠",
"desc": "指定改写我方一个字,随机变成别的字", "desc": "怪物接近我方π时降下雷电消灭怪物",
"icon": "文昌笔", "icon": "御雷珠",
"quality": 3, "quality": 4,
"type": 1, "type": 2,
"functiontype": 11, "functiontype": 11,
"functionpar": "60", "functionpar": "1,10000,10000,3,10",
"weight": 1, "weight": 1,
"price": 120 "price": 150
}, },
{ {
"id": 12, "id": 12,
"name": "云麾号角", "name": "省金契",
"desc": "提振士气,行军速度+15%", "desc": "召唤数字消耗的金币-10%",
"icon": "云麾号角", "icon": "省金契",
"quality": 2, "quality": 2,
"type": 2, "type": 2,
"functiontype": 12, "functiontype": 12,
"functionpar": "1500", "functionpar": 1000,
"weight": 1, "weight": 1,
"price": 90 "price": 90
}, },
{ {
"id": 13, "id": 13,
"name": "万神幡", "name": "滞行牌",
"desc": "拖到我方出兵点,额外征兆一名出兵将领", "desc": "我方怪物行进速度-10%",
"icon": "万神幡", "icon": "滞行牌",
"quality": 3, "quality": 1,
"type": 1, "type": 2,
"functiontype": 13, "functiontype": 13,
"functionpar": "120", "functionpar": 1000,
"weight": 1, "weight": 1,
"price": 120 "price": 40
}, },
{ {
"id": 14, "id": 14,
"name": "还魂丹", "name": "金矿",
"desc": "我方出征兵将死亡后,20%概率原地复活", "desc": "每10S生产一枚金币",
"icon": "还魂丹", "icon": "金矿",
"quality": 2, "quality": 3,
"type": 2, "type": 2,
"functiontype": 14, "functiontype": 14,
"functionpar": "2000", "functionpar": 10,
"weight": 1, "weight": 1,
"price": 90 "price": 90
}, },
{ {
"id": 15, "id": 15,
"name": "补元丹", "name": "能量石",
"desc": "我方出征兵将血量提升10%", "desc": "体力+15",
"icon": "补元丹", "icon": "能量石",
"quality": 2, "quality": 1,
"type": 2, "type": 3,
"functiontype": 15, "functiontype": 15,
"functionpar": "1000", "functionpar": 15,
"weight": 1, "weight": 1,
"price": 90 "price": 40
}, },
{ {
"id": 16, "id": 16,
"name": "妙手神偷", "name": "属性卡",
"desc": "指定偷取敌方一个未合成的字", "desc": "一定几率召唤出属性卡,拖到数字上随机变换一个属性",
"icon": "妙手神偷", "icon": "属性卡",
"quality": 4,
"type": 1,
"functiontype": 16,
"functionpar": "180",
"weight": 1,
"price": 150
},
{
"id": 17,
"name": "攻速符",
"desc": "指定我方单位攻速+40%",
"icon": "攻速符",
"quality": 3, "quality": 3,
"type": 1, "type": 1,
"functiontype": 17, "functiontype": 16,
"functionpar": "60,4000", "functionpar": 1000,
"weight": 1,
"price": 120
},
{
"id": 18,
"name": "九天天鼓",
"desc": "我方所有单位攻速+10%",
"icon": "九天天鼓",
"quality": 2,
"type": 2,
"functiontype": 27,
"functionpar": "1000",
"weight": 1,
"price": 90
},
{
"id": 19,
"name": "千里眼",
"desc": "拖到我方远程单位使用,攻击距离翻倍",
"icon": "千里眼",
"quality": 1,
"type": 1,
"functiontype": 18,
"functionpar": "60",
"weight": 1,
"price": 40
},
{
"id": 20,
"name": "七十二变",
"desc": "每使用一次会随机变成任意一种主动道具",
"icon": "七十二变",
"quality": 4,
"type": 1,
"functiontype": 19,
"functionpar": "60",
"weight": 1,
"price": 150
},
{
"id": 21,
"name": "九霄神雷",
"desc": "敌人接近我方出兵点时降下雷电消灭敌人",
"icon": "九霄神雷",
"quality": 4,
"type": 2,
"functiontype": 20,
"functionpar": "1,10000,5000,3,10",
"weight": 1,
"price": 150
},
{
"id": 22,
"name": "碎法帝钟",
"desc": "50%概率打断boss施法,并反噬boss血量",
"icon": "碎法帝钟",
"quality": 3,
"type": 2,
"functiontype": 21,
"functionpar": "5000,3300",
"weight": 1, "weight": 1,
"price": 120 "price": 120
},
{
"id": 23,
"name": "幻魂镜",
"desc": "指定迷惑敌方1名神将,使其暂停攻击3秒",
"icon": "幻魂镜",
"quality": 2,
"type": 1,
"functiontype": 22,
"functionpar": "60,3",
"weight": 1,
"price": 90
},
{
"id": 24,
"name": "弱水",
"desc": "弱水沉重,敌方士兵移动速度-20%",
"icon": "弱水",
"quality": 1,
"type": 2,
"functiontype": 23,
"functionpar": "2000",
"weight": 1,
"price": 40
},
{
"id": 25,
"name": "三昧真火",
"desc": "拖到道路上,触碰敌人爆发伤害",
"icon": "三昧真火",
"quality": 1,
"type": 1,
"functiontype": 24,
"functionpar": "60,200000,300",
"weight": 1,
"price": 40
},
{
"id": 26,
"name": "武财神",
"desc": "杀敌后,20%概率多获得一个蟠桃",
"icon": "武财神",
"quality": 2,
"type": 2,
"functiontype": 25,
"functionpar": "2000,1",
"weight": 1,
"price": 90
},
{
"id": 27,
"name": "文财神",
"desc": "召唤消耗蟠桃数量-10%",
"icon": "文财神",
"quality": 2,
"type": 2,
"functiontype": 26,
"functionpar": "1000",
"weight": 1,
"price": 90
} }
] ]

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
{"ver":"1.2.0","importer":"directory","imported":true,"uuid":"f9c11b7b-02d2-4c9e-ae88-d6d3a6585102","files":[],"subMetas":{},"userData":{}}

@ -0,0 +1 @@
{"ver":"1.2.0","importer":"directory","imported":true,"uuid":"e67dabc8-8016-454d-8c48-ab138e4b241e","files":[],"subMetas":{},"userData":{}}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "8f6d72a3-b4d7-4186-ac66-329ae38c8651",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "PopGemInfo"
}
}

@ -35,6 +35,9 @@
}, },
{ {
"__id__": 72 "__id__": 72
},
{
"__id__": 75
} }
], ],
"_prefab": { "_prefab": {
@ -1491,5 +1494,23 @@
"__id__": 28 "__id__": 28
} }
] ]
},
{
"__type__": "b23e9blLAVI7bsjEad+g/s+",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 1
},
"_enabled": true,
"__prefab": {
"__id__": 76
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "b8KpGetGemUiSc01abCdEf"
} }
] ]

@ -141,7 +141,8 @@ export const checkRedDotShow = (id: RedDotID) => {
} }
if (id == RedDotID.主界面_武器) { if (id == RedDotID.主界面_武器) {
return gg.equip.hasNewWeaponBadgeRedDot() ? 1 : 0; // 宝石库:有可合成宝石时显示
return gg.gem.canFuseAny() ? 1 : 0;
} }
if (id == RedDotID.主界面_挂机) { if (id == RedDotID.主界面_挂机) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -2,7 +2,7 @@
"ver": "1.0.27", "ver": "1.0.27",
"importer": "image", "importer": "image",
"imported": true, "imported": true,
"uuid": "abf7f345-5a6f-4efe-ace1-5cb998f610d8", "uuid": "b9319e63-3d94-4217-86cf-e3716fab5f2e",
"files": [ "files": [
".json", ".json",
".png" ".png"
@ -10,14 +10,14 @@
"subMetas": { "subMetas": {
"6c48a": { "6c48a": {
"importer": "texture", "importer": "texture",
"uuid": "abf7f345-5a6f-4efe-ace1-5cb998f610d8@6c48a", "uuid": "b9319e63-3d94-4217-86cf-e3716fab5f2e@6c48a",
"displayName": "矩形 798 拷贝 2-1", "displayName": "英雄信息框",
"id": "6c48a", "id": "6c48a",
"name": "texture", "name": "texture",
"userData": { "userData": {
"wrapModeS": "clamp-to-edge", "wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge", "wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "abf7f345-5a6f-4efe-ace1-5cb998f610d8", "imageUuidOrDatabaseUri": "b9319e63-3d94-4217-86cf-e3716fab5f2e",
"isUuid": true, "isUuid": true,
"visible": false, "visible": false,
"minfilter": "linear", "minfilter": "linear",
@ -34,8 +34,8 @@
}, },
"f9941": { "f9941": {
"importer": "sprite-frame", "importer": "sprite-frame",
"uuid": "abf7f345-5a6f-4efe-ace1-5cb998f610d8@f9941", "uuid": "b9319e63-3d94-4217-86cf-e3716fab5f2e@f9941",
"displayName": "矩形 798 拷贝 2-1", "displayName": "英雄信息框",
"id": "f9941", "id": "f9941",
"name": "spriteFrame", "name": "spriteFrame",
"userData": { "userData": {
@ -45,10 +45,10 @@
"offsetY": 0, "offsetY": 0,
"trimX": 0, "trimX": 0,
"trimY": 0, "trimY": 0,
"width": 62, "width": 517,
"height": 62, "height": 822,
"rawWidth": 62, "rawWidth": 517,
"rawHeight": 62, "rawHeight": 822,
"borderTop": 0, "borderTop": 0,
"borderBottom": 0, "borderBottom": 0,
"borderLeft": 0, "borderLeft": 0,
@ -60,17 +60,17 @@
"meshType": 0, "meshType": 0,
"vertices": { "vertices": {
"rawPosition": [ "rawPosition": [
-31, -258.5,
-31, -411,
0, 0,
31, 258.5,
-31, -411,
0, 0,
-31, -258.5,
31, 411,
0, 0,
31, 258.5,
31, 411,
0 0
], ],
"indexes": [ "indexes": [
@ -83,12 +83,12 @@
], ],
"uv": [ "uv": [
0, 0,
62, 822,
62, 517,
62, 822,
0, 0,
0, 0,
62, 517,
0 0
], ],
"nuv": [ "nuv": [
@ -102,18 +102,18 @@
1 1
], ],
"minPos": [ "minPos": [
-31, -258.5,
-31, -411,
0 0
], ],
"maxPos": [ "maxPos": [
31, 258.5,
31, 411,
0 0
] ]
}, },
"isUuid": true, "isUuid": true,
"imageUuidOrDatabaseUri": "abf7f345-5a6f-4efe-ace1-5cb998f610d8@6c48a", "imageUuidOrDatabaseUri": "b9319e63-3d94-4217-86cf-e3716fab5f2e@6c48a",
"atlasUuid": "", "atlasUuid": "",
"trimType": "auto" "trimType": "auto"
}, },
@ -129,6 +129,6 @@
"type": "sprite-frame", "type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false, "fixAlphaTransparencyArtifacts": false,
"hasAlpha": true, "hasAlpha": true,
"redirect": "abf7f345-5a6f-4efe-ace1-5cb998f610d8@6c48a" "redirect": "b9319e63-3d94-4217-86cf-e3716fab5f2e@6c48a"
} }
} }

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "8e6253a0-a91d-49e1-bbb9-aa09d3e7e05b",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "8e6253a0-a91d-49e1-bbb9-aa09d3e7e05b@6c48a",
"displayName": "武器信息图标底框--绿",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "8e6253a0-a91d-49e1-bbb9-aa09d3e7e05b",
"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": "8e6253a0-a91d-49e1-bbb9-aa09d3e7e05b@f9941",
"displayName": "武器信息图标底框--绿",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 242,
"height": 243,
"rawWidth": 242,
"rawHeight": 243,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-121,
-121.5,
0,
121,
-121.5,
0,
-121,
121.5,
0,
121,
121.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
243,
242,
243,
0,
0,
242,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-121,
-121.5,
0
],
"maxPos": [
121,
121.5,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "8e6253a0-a91d-49e1-bbb9-aa09d3e7e05b@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "8e6253a0-a91d-49e1-bbb9-aa09d3e7e05b@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "d4c1af99-1106-478e-9c7c-a1b75097d966",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "d4c1af99-1106-478e-9c7c-a1b75097d966@6c48a",
"displayName": "武器信息图标底框-橙",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "d4c1af99-1106-478e-9c7c-a1b75097d966",
"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": "d4c1af99-1106-478e-9c7c-a1b75097d966@f9941",
"displayName": "武器信息图标底框-橙",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 242,
"height": 243,
"rawWidth": 242,
"rawHeight": 243,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-121,
-121.5,
0,
121,
-121.5,
0,
-121,
121.5,
0,
121,
121.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
243,
242,
243,
0,
0,
242,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-121,
-121.5,
0
],
"maxPos": [
121,
121.5,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "d4c1af99-1106-478e-9c7c-a1b75097d966@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "d4c1af99-1106-478e-9c7c-a1b75097d966@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "eff7397b-07f2-4843-b660-d491f2da6928",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "eff7397b-07f2-4843-b660-d491f2da6928@6c48a",
"displayName": "武器信息图标底框-紫",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "eff7397b-07f2-4843-b660-d491f2da6928",
"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": "eff7397b-07f2-4843-b660-d491f2da6928@f9941",
"displayName": "武器信息图标底框-紫",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 242,
"height": 243,
"rawWidth": 242,
"rawHeight": 243,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-121,
-121.5,
0,
121,
-121.5,
0,
-121,
121.5,
0,
121,
121.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
243,
242,
243,
0,
0,
242,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-121,
-121.5,
0
],
"maxPos": [
121,
121.5,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "eff7397b-07f2-4843-b660-d491f2da6928@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "eff7397b-07f2-4843-b660-d491f2da6928@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "c3597ab8-38c4-4667-bf19-9e5279928276",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "c3597ab8-38c4-4667-bf19-9e5279928276@6c48a",
"displayName": "武器信息图标底框-红",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "c3597ab8-38c4-4667-bf19-9e5279928276",
"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": "c3597ab8-38c4-4667-bf19-9e5279928276@f9941",
"displayName": "武器信息图标底框-红",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 242,
"height": 243,
"rawWidth": 242,
"rawHeight": 243,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-121,
-121.5,
0,
121,
-121.5,
0,
-121,
121.5,
0,
121,
121.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
243,
242,
243,
0,
0,
242,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-121,
-121.5,
0
],
"maxPos": [
121,
121.5,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "c3597ab8-38c4-4667-bf19-9e5279928276@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "c3597ab8-38c4-4667-bf19-9e5279928276@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "b8402838-97ca-49f8-a800-9ac9f59d710f",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "b8402838-97ca-49f8-a800-9ac9f59d710f@6c48a",
"displayName": "武器信息图标底框-蓝",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "b8402838-97ca-49f8-a800-9ac9f59d710f",
"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": "b8402838-97ca-49f8-a800-9ac9f59d710f@f9941",
"displayName": "武器信息图标底框-蓝",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 242,
"height": 243,
"rawWidth": 242,
"rawHeight": 243,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-121,
-121.5,
0,
121,
-121.5,
0,
-121,
121.5,
0,
121,
121.5,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
243,
242,
243,
0,
0,
242,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-121,
-121.5,
0
],
"maxPos": [
121,
121.5,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "b8402838-97ca-49f8-a800-9ac9f59d710f@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "b8402838-97ca-49f8-a800-9ac9f59d710f@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "025c6b9d-1242-40ed-9d79-96e7bc953c29",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "025c6b9d-1242-40ed-9d79-96e7bc953c29@6c48a",
"displayName": "矩形 798 拷贝 2",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "025c6b9d-1242-40ed-9d79-96e7bc953c29",
"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": "025c6b9d-1242-40ed-9d79-96e7bc953c29@f9941",
"displayName": "矩形 798 拷贝 2",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 63,
"height": 62,
"rawWidth": 63,
"rawHeight": 62,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-31.5,
-31,
0,
31.5,
-31,
0,
-31.5,
31,
0,
31.5,
31,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
62,
63,
62,
0,
0,
63,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-31.5,
-31,
0
],
"maxPos": [
31.5,
31,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "025c6b9d-1242-40ed-9d79-96e7bc953c29@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "025c6b9d-1242-40ed-9d79-96e7bc953c29@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "ea4d4f68-8b22-44c5-8a22-6dcf096e80c8",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "ea4d4f68-8b22-44c5-8a22-6dcf096e80c8@6c48a",
"displayName": "矩形 798 拷贝 3-1",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "ea4d4f68-8b22-44c5-8a22-6dcf096e80c8",
"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": "ea4d4f68-8b22-44c5-8a22-6dcf096e80c8@f9941",
"displayName": "矩形 798 拷贝 3-1",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 62,
"height": 62,
"rawWidth": 62,
"rawHeight": 62,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-31,
-31,
0,
31,
-31,
0,
-31,
31,
0,
31,
31,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
62,
62,
62,
0,
0,
62,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-31,
-31,
0
],
"maxPos": [
31,
31,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "ea4d4f68-8b22-44c5-8a22-6dcf096e80c8@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "ea4d4f68-8b22-44c5-8a22-6dcf096e80c8@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "8bce9335-cd65-40a4-9c1f-72c084304fff",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "8bce9335-cd65-40a4-9c1f-72c084304fff@6c48a",
"displayName": "矩形 798 拷贝 3",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "8bce9335-cd65-40a4-9c1f-72c084304fff",
"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": "8bce9335-cd65-40a4-9c1f-72c084304fff@f9941",
"displayName": "矩形 798 拷贝 3",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 62,
"height": 62,
"rawWidth": 62,
"rawHeight": 62,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-31,
-31,
0,
31,
-31,
0,
-31,
31,
0,
31,
31,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
62,
62,
62,
0,
0,
62,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-31,
-31,
0
],
"maxPos": [
31,
31,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "8bce9335-cd65-40a4-9c1f-72c084304fff@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "8bce9335-cd65-40a4-9c1f-72c084304fff@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "5a1adc0b-057a-4c1f-9361-138325e51bb1",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "5a1adc0b-057a-4c1f-9361-138325e51bb1@6c48a",
"displayName": "矩形 798 拷贝-1",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "5a1adc0b-057a-4c1f-9361-138325e51bb1",
"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": "5a1adc0b-057a-4c1f-9361-138325e51bb1@f9941",
"displayName": "矩形 798 拷贝-1",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 62,
"height": 62,
"rawWidth": 62,
"rawHeight": 62,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-31,
-31,
0,
31,
-31,
0,
-31,
31,
0,
31,
31,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
62,
62,
62,
0,
0,
62,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-31,
-31,
0
],
"maxPos": [
31,
31,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "5a1adc0b-057a-4c1f-9361-138325e51bb1@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "5a1adc0b-057a-4c1f-9361-138325e51bb1@6c48a"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

@ -1,134 +0,0 @@
{
"ver": "1.0.27",
"importer": "image",
"imported": true,
"uuid": "8d14085d-8f15-4d38-8419-7cb5f82614fb",
"files": [
".json",
".png"
],
"subMetas": {
"6c48a": {
"importer": "texture",
"uuid": "8d14085d-8f15-4d38-8419-7cb5f82614fb@6c48a",
"displayName": "矩形 798 拷贝",
"id": "6c48a",
"name": "texture",
"userData": {
"wrapModeS": "clamp-to-edge",
"wrapModeT": "clamp-to-edge",
"imageUuidOrDatabaseUri": "8d14085d-8f15-4d38-8419-7cb5f82614fb",
"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": "8d14085d-8f15-4d38-8419-7cb5f82614fb@f9941",
"displayName": "矩形 798 拷贝",
"id": "f9941",
"name": "spriteFrame",
"userData": {
"trimThreshold": 1,
"rotated": false,
"offsetX": 0,
"offsetY": 0,
"trimX": 0,
"trimY": 0,
"width": 63,
"height": 62,
"rawWidth": 63,
"rawHeight": 62,
"borderTop": 0,
"borderBottom": 0,
"borderLeft": 0,
"borderRight": 0,
"packable": true,
"pixelsToUnit": 100,
"pivotX": 0.5,
"pivotY": 0.5,
"meshType": 0,
"vertices": {
"rawPosition": [
-31.5,
-31,
0,
31.5,
-31,
0,
-31.5,
31,
0,
31.5,
31,
0
],
"indexes": [
0,
1,
2,
2,
1,
3
],
"uv": [
0,
62,
63,
62,
0,
0,
63,
0
],
"nuv": [
0,
0,
1,
0,
0,
1,
1,
1
],
"minPos": [
-31.5,
-31,
0
],
"maxPos": [
31.5,
31,
0
]
},
"isUuid": true,
"imageUuidOrDatabaseUri": "8d14085d-8f15-4d38-8419-7cb5f82614fb@6c48a",
"atlasUuid": "",
"trimType": "auto"
},
"ver": "1.0.12",
"imported": true,
"files": [
".json"
],
"subMetas": {}
}
},
"userData": {
"type": "sprite-frame",
"fixAlphaTransparencyArtifacts": false,
"hasAlpha": true,
"redirect": "8d14085d-8f15-4d38-8419-7cb5f82614fb@6c48a"
}
}

@ -0,0 +1,160 @@
import type { BattleSession } from 'db://assets/mx/module/battle';
import type { EntityId } from 'db://assets/mx/module/ecs';
import { AttrId, AttributeComp } from 'db://assets/mx/module/battle/ecs/Components';
import type { Player } from '../../game/Player';
import { TableNames } from '../../game/ConfigTableData';
import { parseEntryBaseAttr } from '../skill/SkillEntryParse';
const LOG = '[GemBattle]';
/** modifier.source 前缀,刷新时整批清除 */
const SOURCE_PREFIX = 'gem_';
/** 五行镶嵌解析结果(局内用) */
export interface IGemBattleResolve {
/** 提供技能的宝石 id(0=无) */
skillGemId: number;
/** Skill.id(0=无) */
skillId: number;
/** 各槽属性条目(含来源槽,便于 source 去重) */
attrs: IGemBattleAttr[];
}
/** 单条宝石属性 */
export interface IGemBattleAttr {
/** Weapon.attritubes 类型 1~5 */
type: number;
/** 万分比值 */
value: number;
/** 宝石 id */
gemId: number;
/** 槽位 0~3 */
slot: number;
}
/**
* 局内宝石生效:读镶嵌 → 属性 modifier → 技能宝石 id。
* 仅本机玩家;AI 返回空。
*/
export class GemBattleEffect {
/**
* 解析某五行已镶嵌宝石。
* @param element Hero.herotype / 镶嵌 element(1~5)
* @param player 部署所属玩家
*/
static resolve(element: number, player: Player | null | undefined): IGemBattleResolve {
const empty: IGemBattleResolve = { skillGemId: 0, skillId: 0, attrs: [] };
if (!player || !player.isLocal) return empty;
const e = Math.floor(Number(element) || 0);
if (e < 1 || e > 5) return empty;
let slots: number[];
try {
slots = gg.gem.getElementSlots(e);
} catch (err) {
console.warn(LOG, 'getElementSlots 失败', e, err);
return empty;
}
const attrs: IGemBattleAttr[] = [];
let skillGemId = 0;
let skillId = 0;
let skillQuality = -1;
for (let slot = 0; slot < slots.length; slot++) {
const gemId = Math.floor(Number(slots[slot]) || 0);
if (gemId <= 0) continue;
const row = gg.data.table.getTableData<ITableWeapon>(TableNames.Weapon, gemId);
if (!row) {
console.warn(LOG, '宝石配表缺失', gemId, 'element', e, 'slot', slot);
continue;
}
const parsed = parseEntryBaseAttr(String(row.attritubes ?? ''));
for (let i = 0; i < parsed.length; i++) {
const p = parsed[i];
if (p.type < 1 || p.type > 5) {
console.warn(LOG, 'attritubes 类型非法', gemId, p.type, row.attritubes);
continue;
}
if (!(p.value > 0)) {
console.warn(LOG, 'attritubes 参数非法', gemId, row.attritubes);
continue;
}
attrs.push({ type: p.type, value: p.value, gemId, slot });
}
const sid = Math.floor(Number(row.act_skill) || 0);
if (sid <= 0) continue;
const q = Math.floor(Number(row.quality) || 0);
if (skillId > 0) {
console.warn(LOG, '同五行多颗技能宝石,取品质更高者', e, 'prev', skillGemId, 'next', gemId);
}
if (skillId <= 0 || q > skillQuality || (q === skillQuality && gemId > skillGemId)) {
skillGemId = gemId;
skillId = sid;
skillQuality = q;
}
}
return { skillGemId, skillId, attrs };
}
/**
* 清除实体上全部宝石属性 modifier。
* @param session 战斗会话
* @param entityId 塔实体
*/
static clear(session: BattleSession, entityId: EntityId): void {
const attr = session.world.getComponent(entityId, AttributeComp);
if (!attr) return;
attr.modifiers = attr.modifiers.filter(m => !String(m.source ?? '').startsWith(SOURCE_PREFIX));
}
/**
* 将解析结果写入属性(先 clear 再挂)。
* @param session 战斗会话
* @param entityId 塔实体
* @param resolved 镶嵌解析
*/
static apply(session: BattleSession, entityId: EntityId, resolved: IGemBattleResolve): void {
this.clear(session, entityId);
if (!resolved.attrs.length) return;
if (!session.world.isAlive(entityId)) return;
if (!session.world.getComponent(entityId, AttributeComp)) {
session.world.addComponent(entityId, new AttributeComp());
}
const attrSvc = session.attributes;
for (let i = 0; i < resolved.attrs.length; i++) {
const a = resolved.attrs[i];
const source = `${SOURCE_PREFIX}${a.gemId}_s${a.slot}_${i}`;
const wan = a.value;
switch (a.type) {
case 1:
attrSvc.addModifier(entityId, {
attr: AttrId.Attack, op: 'Mul', value: 1 + wan / 10000, source,
});
break;
case 2:
attrSvc.addModifier(entityId, {
attr: AttrId.CritRate, op: 'Add', value: wan / 10000, source,
});
break;
case 3:
attrSvc.addModifier(entityId, {
attr: AttrId.CritDamage, op: 'Add', value: wan / 10000, source,
});
break;
case 4:
attrSvc.addModifier(entityId, {
attr: 'aspd_add', op: 'Add', value: wan, source,
});
break;
case 5:
attrSvc.addModifier(entityId, {
attr: 'dmg_mul', op: 'Add', value: wan / 10000, source,
});
break;
default:
console.warn(LOG, '未处理属性类型', a.type, a.gemId);
break;
}
}
}
}

@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "e16eb3f3-e692-45bb-98db-a0bdf813113f",
"files": [],
"subMetas": {},
"userData": {}
}

@ -25,6 +25,7 @@ import { isAtkDebugWeapon } from '../debug/AtkDebugConstants';
import { getPassiveSkillHost } from './PassiveSkillRuntime'; import { getPassiveSkillHost } from './PassiveSkillRuntime';
import { SkillAtkBuffExecutor } from './SkillAtkBuffExecutor'; import { SkillAtkBuffExecutor } from './SkillAtkBuffExecutor';
import { TableAnomaly } from '../../game/TableAnomaly'; import { TableAnomaly } from '../../game/TableAnomaly';
import { GemBattleEffect } from './GemBattleEffect';
/** AI 侧武将武器解析(由 AiBattleBridge start/stop 注册,避免循环 require) */ /** AI 侧武将武器解析(由 AiBattleBridge start/stop 注册,避免循环 require) */
let _aiHeroWeaponResolver: ((heroId: number) => number | null) | null = null; let _aiHeroWeaponResolver: ((heroId: number) => number | null) | null = null;
@ -37,10 +38,18 @@ export function setAiHeroWeaponResolver(resolver: ((heroId: number) => number |
_aiHeroWeaponResolver = resolver; _aiHeroWeaponResolver = resolver;
} }
/** 解析英雄当前战斗武器 id(本机走装备;AI 走匹配档案掷骰)。五行数字只用 herotype 预制体,不查 Weapon 表。 */ /**
* 解析英雄当前战斗武器/技能宝石 id。
* 五行数字:本机读镶嵌技能宝石;非五行:本机装备 / AI 掷骰。
*/
export function resolveHeroBattleWeaponId(heroId: number, player?: Player | null): number { export function resolveHeroBattleWeaponId(heroId: number, player?: Player | null): number {
const hero = gg.data.table.getTableData<ITableHero>(TableNames.Hero, heroId); const hero = gg.data.table.getTableData<ITableHero>(TableNames.Hero, heroId);
if (isElementHero(hero)) return 0; if (isElementHero(hero)) {
if (player?.isLocal) {
return GemBattleEffect.resolve(Math.floor(Number(hero!.herotype)), player).skillGemId;
}
return 0;
}
if (player && !player.isLocal) { if (player && !player.isLocal) {
if (_aiHeroWeaponResolver) { if (_aiHeroWeaponResolver) {
const aiId = _aiHeroWeaponResolver(heroId); const aiId = _aiHeroWeaponResolver(heroId);
@ -68,11 +77,19 @@ export class TowerLoadoutService {
initEntity(entityId: EntityId, heroId: number, level: number, player: Player): void { initEntity(entityId: EntityId, heroId: number, level: number, player: Player): void {
const hero = gg.data.table.getTableData<ITableHero>(TableNames.Hero, heroId); const hero = gg.data.table.getTableData<ITableHero>(TableNames.Hero, heroId);
if (!hero) return; if (!hero) return;
const weaponId = resolveHeroBattleWeaponId(heroId, player); let weaponId = 0;
const weaponRow = weaponId > 0 let weaponSkillId = 0;
? gg.data.table.getTableData<ITableWeapon>(TableNames.Weapon, weaponId) if (isElementHero(hero)) {
: null; const gem = GemBattleEffect.resolve(Math.floor(Number(hero.herotype)), player);
const weaponSkillId = Number(weaponRow?.act_skill) || 0; weaponId = gem.skillGemId;
weaponSkillId = gem.skillId;
} else {
weaponId = resolveHeroBattleWeaponId(heroId, player);
const weaponRow = weaponId > 0
? gg.data.table.getTableData<ITableWeapon>(TableNames.Weapon, weaponId)
: null;
weaponSkillId = Number(weaponRow?.act_skill) || 0;
}
let weaponSkillActive = false; let weaponSkillActive = false;
if (weaponSkillId > 0) { if (weaponSkillId > 0) {
const sk = gg.data.table.getTableData<ITableSkill>(TableNames.Skill, weaponSkillId); const sk = gg.data.table.getTableData<ITableSkill>(TableNames.Skill, weaponSkillId);
@ -406,6 +423,12 @@ export class TowerLoadoutService {
this.initEntity(entityId, weapon1Id, lv, player); this.initEntity(entityId, weapon1Id, lv, player);
const loadout = this.getLoadout(entityId)!; const loadout = this.getLoadout(entityId)!;
if (isElementHero(hero)) {
const gem = GemBattleEffect.resolve(Math.floor(Number(hero.herotype)), player);
GemBattleEffect.apply(session, entityId, gem);
} else {
GemBattleEffect.clear(session, entityId);
}
this.applyAbilities(session, entityId, loadout); this.applyAbilities(session, entityId, loadout);
if (!isRefresh && loadout.weaponSkillId > 0 && !loadout.weaponSkillActive) { if (!isRefresh && loadout.weaponSkillId > 0 && !loadout.weaponSkillActive) {

@ -8,7 +8,7 @@ export interface TowerLoadout {
weapon1Id: number; weapon1Id: number;
/** 合成等级 */ /** 合成等级 */
level: number; level: number;
/** 当前装备武器 id(穿戴或 startweapon) */ /** 武器/技能宝石 id(名将:穿戴或 startweapon;五行:镶嵌技能宝石) */
weaponId: number; weaponId: number;
/** 普攻 Skill.id */ /** 普攻 Skill.id */
normalBaseAtkId: number; normalBaseAtkId: number;

@ -180,6 +180,8 @@ export function deriveTowerSetupStats(w1: ITableHero, composeLevel: number, batt
*/ */
function resolveSetupWeaponId(w1: ITableHero, battleWeaponId?: number): number { function resolveSetupWeaponId(w1: ITableHero, battleWeaponId?: number): number {
if (typeof battleWeaponId === 'number' && battleWeaponId > 0) return battleWeaponId; if (typeof battleWeaponId === 'number' && battleWeaponId > 0) return battleWeaponId;
// 五行数字:技能宝石由调用方传入;无则 0,禁止回落到名将装备
if (isElementHero(w1)) return 0;
const wornId = gg.equip.getHeroEquipId(w1.id); const wornId = gg.equip.getHeroEquipId(w1.id);
if (wornId > 0) return wornId; if (wornId > 0) return wornId;
return Number(w1.startweapon) || 0; return Number(w1.startweapon) || 0;

@ -6273,10 +6273,10 @@ if (weapon2Id === GRID_WEAPON2_ID) return true;
this._textInteractDedupKey = dedupKey; this._textInteractDedupKey = dedupKey;
const data = this._resolveTextGridData(grid); const data = this._resolveTextGridData(grid);
const isSingleType = data.tileType === Weapon2TileType.Single;
const isPeachTree = grid.isPeachTreeTile(); const isPeachTree = grid.isPeachTreeTile();
const opensDesc1 = isSingleType || isPeachTree; // 五行数字与旧 Single=1 编号重叠,数字格子一律 Hero,勿再开 Desc1
const hasHeroDesc = !!(data.tower && data.weapon1Id > 0 && !isSingleType && !isPeachTree); const opensDesc1 = isPeachTree;
const hasHeroDesc = !isPeachTree && !!(data.tower && data.weapon1Id > 0);
const canRange = this._canShowType1Range(grid); const canRange = this._canShowType1Range(grid);
if ( if (
@ -6483,7 +6483,6 @@ if (weapon2Id === GRID_WEAPON2_ID) return true;
private _gridCanShowWeaponDesc(grid: TextGrid): boolean { private _gridCanShowWeaponDesc(grid: TextGrid): boolean {
const data = this._resolveTextGridData(grid); const data = this._resolveTextGridData(grid);
if (grid.isPeachTreeTile()) return !!data.tile; if (grid.isPeachTreeTile()) return !!data.tile;
if (data.tileType === Weapon2TileType.Single) return !!data.tile;
return !!(data.tower && data.weapon1Id > 0); return !!(data.tower && data.weapon1Id > 0);
} }

@ -1,17 +1,22 @@
import { _decorator, Component, Label, Node, Sprite } from 'cc'; import { _decorator, Component, EventTouch, Label, Node, Sprite, Vec2 } from 'cc';
import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad'; import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad';
import { GBundle } from '../game/ConfigRes'; import { GBundle, UIID } from '../game/ConfigRes';
const { ccclass } = _decorator; const { ccclass } = _decorator;
const LOG = '[GemGrid]'; const LOG = '[GemGrid]';
/** 超过此位移视为拖拽,不弹信息 */
const TAP_THRESHOLD = 18;
/** /**
* 宝石格子:仓库 / 获得弹窗共用。 * 宝石格子:仓库 / 获得弹窗共用。
* bg=宝石框小{quality};icon=Weapon.icon;lbNum=数量。 * bg=宝石框大{quality};icon=Weapon.icon;lbNum=数量。
* 轻点打开 PopGemInfo(拖拽不触发)。
*/ */
@ccclass('GemGrid') @ccclass('GemGrid')
export class GemGrid extends Component { export class GemGrid extends Component {
/** 是否响应轻点打开信息 */
canClick = true;
/** 品质框 */ /** 品质框 */
bg: Node = null!; bg: Node = null!;
/** 宝石图标 */ /** 宝石图标 */
@ -21,10 +26,36 @@ export class GemGrid extends Component {
/** 当前宝石配表 */ /** 当前宝石配表 */
data: ITableWeapon | null = null; data: ITableWeapon | null = null;
private _pressing = false;
private _pressStart = new Vec2();
protected onLoad(): void { protected onLoad(): void {
this._bindNodes(); this._bindNodes();
} }
protected onEnable(): void {
this.node.on(Node.EventType.TOUCH_START, this._onTouchStart, this);
this.node.on(Node.EventType.TOUCH_END, this._onTouchEnd, this);
this.node.on(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
}
protected onDisable(): void {
this.node.off(Node.EventType.TOUCH_START, this._onTouchStart, this);
this.node.off(Node.EventType.TOUCH_END, this._onTouchEnd, this);
this.node.off(Node.EventType.TOUCH_CANCEL, this._onTouchCancel, this);
this._pressing = false;
}
/**
* 打开宝石信息弹窗。
* @param gemId Weapon.id
*/
static openInfo(gemId: number): void {
const id = Math.floor(Number(gemId) || 0);
if (id <= 0) return;
void gg.ui.openPanel(UIID.PopGemInfo, { data: id });
}
/** /**
* 绑定展示数据。 * 绑定展示数据。
* @param data Weapon 行;null 表示空 * @param data Weapon 行;null 表示空
@ -34,12 +65,18 @@ export class GemGrid extends Component {
this._bindNodes(); this._bindNodes();
this.data = data; this.data = data;
const quality = data ? Math.floor(Number(data.quality) || 0) : 0; const quality = data ? Math.floor(Number(data.quality) || 0) : 0;
if (quality < 0 || quality > 5) console.warn(LOG, '品质越界', data?.id, quality); if (!data) {
this._setSprite(this.bg, `weapon/宝石框小${Math.max(0, Math.min(5, quality))}`); this._setSprite(this.bg, '', false);
const iconName = data ? String(data.icon || '').trim() : ''; this._setSprite(this.icon, '', true);
if (data && !iconName) console.warn(LOG, 'icon 缺失', data.id); if (this.icon?.isValid) this.icon.active = false;
this._setSprite(this.icon, iconName ? `weapon/${iconName}` : ''); } else {
if (this.icon?.isValid) this.icon.active = !!iconName; if (quality < 1 || quality > 5) console.warn(LOG, '品质无对应宝石框大', data.id, quality);
this._setSprite(this.bg, `weapon/宝石框大${quality}`, false);
const iconName = String(data.icon || '').trim();
if (!iconName) console.warn(LOG, 'icon 缺失', data.id);
this._setSprite(this.icon, iconName ? `weapon/${iconName}` : '', true);
if (this.icon?.isValid) this.icon.active = !!iconName;
}
if (this.lbNum?.isValid) { if (this.lbNum?.isValid) {
const showNum = count >= 0; const showNum = count >= 0;
this.lbNum.node.active = showNum; this.lbNum.node.active = showNum;
@ -47,6 +84,29 @@ export class GemGrid extends Component {
} }
} }
/** 按下 */
private _onTouchStart(e: EventTouch): void {
this._pressing = true;
this._pressStart.set(e.getUILocation());
}
/** 抬起:位移未超阈值则打开信息 */
private _onTouchEnd(e: EventTouch): void {
if (!this._pressing) return;
this._pressing = false;
if (!this.canClick || !this.data) return;
const loc = e.getUILocation();
const dx = loc.x - this._pressStart.x;
const dy = loc.y - this._pressStart.y;
if (dx * dx + dy * dy >= TAP_THRESHOLD * TAP_THRESHOLD) return;
GemGrid.openInfo(Number(this.data.id) || 0);
}
/** 取消按下 */
private _onTouchCancel(): void {
this._pressing = false;
}
/** 绑定子节点 */ /** 绑定子节点 */
private _bindNodes(): void { private _bindNodes(): void {
if (!this.bg?.isValid) this.bg = this.node.getChildByName('bg')!; if (!this.bg?.isValid) this.bg = this.node.getChildByName('bg')!;
@ -55,18 +115,31 @@ export class GemGrid extends Component {
const n = this.node.getChildByName('lbNum'); const n = this.node.getChildByName('lbNum');
this.lbNum = n?.getComponent(Label) ?? null!; this.lbNum = n?.getComponent(Label) ?? null!;
} }
if (!this.bg) console.warn(LOG, 'bg 节点缺失', this.node?.name);
} }
/** icons/weapon 下图 */ /**
private _setSprite(node: Node | null, path: string): void { * icons/weapon 下图。
* @param nativeSize true=按贴图原始尺寸(不拉伸);false=铺满节点 UITransform
*/
private _setSprite(node: Node | null, path: string, nativeSize: boolean): void {
if (!node?.isValid) return; if (!node?.isValid) return;
if (!path) { if (!path) {
node.active = false;
node.getComponent(SpriteLoad)?.clear(); node.getComponent(SpriteLoad)?.clear();
const sp = node.getComponent(Sprite); const sp = node.getComponent(Sprite);
if (sp) sp.spriteFrame = null; if (sp) sp.spriteFrame = null;
return; return;
} }
node.active = true;
if (!node.getComponent(Sprite)) node.addComponent(Sprite); if (!node.getComponent(Sprite)) node.addComponent(Sprite);
node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items); const sp = node.getComponent(Sprite)!;
sp.type = Sprite.Type.SIMPLE;
// icon 用 TRIMMED 跟贴图尺寸,避免 CUSTOM 拉伸变形
sp.sizeMode = nativeSize ? Sprite.SizeMode.TRIMMED : Sprite.SizeMode.CUSTOM;
node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items, () => {
if (!node.isValid || !sp.isValid) return;
sp.sizeMode = nativeSize ? Sprite.SizeMode.TRIMMED : Sprite.SizeMode.CUSTOM;
});
} }
} }

@ -334,6 +334,8 @@ export enum UIID {
PanelGemstone = 691, PanelGemstone = 691,
/** 获得新宝石弹窗 */ /** 获得新宝石弹窗 */
PopGetGem = 692, PopGetGem = 692,
/** 宝石信息弹窗 */
PopGemInfo = 693,
} }
@ -407,6 +409,8 @@ export const UIConfigs: { [key: number]: IUIConfig } = {
[UIID.PanelGemstone]: { desc: "宝石仓库页面", layer: UILayer.Page, bundle: GBundle.Gemstone, path: GPath.UIPrefab, name: "PanelGemstone" }, [UIID.PanelGemstone]: { desc: "宝石仓库页面", layer: UILayer.Page, bundle: GBundle.Gemstone, path: GPath.UIPrefab, name: "PanelGemstone" },
/**获得新宝石弹窗 */ /**获得新宝石弹窗 */
[UIID.PopGetGem]: { desc: "获得新宝石", layer: UILayer.Pop, bundle: GBundle.PopCommon, path: GPath.popPrefab('popGetGem'), name: "PopGetGem", popAnim: POP_ANIM.standard }, [UIID.PopGetGem]: { desc: "获得新宝石", layer: UILayer.Pop, bundle: GBundle.PopCommon, path: GPath.popPrefab('popGetGem'), name: "PopGetGem", popAnim: POP_ANIM.standard },
/**宝石信息弹窗 */
[UIID.PopGemInfo]: { desc: "宝石信息", layer: UILayer.Pop, bundle: GBundle.PopCommon, path: GPath.popPrefab('popGemInfo'), name: "PopGemInfo", popAnim: POP_ANIM.standard },
} }
/** @deprecated 请从 db://assets/mx/module/res/ResPkgName 导入 */ /** @deprecated 请从 db://assets/mx/module/res/ResPkgName 导入 */

@ -246,6 +246,7 @@ export class GM extends Component {
'今日首次登录(每日重置)', '今日首次登录(每日重置)',
'复制存档', '复制存档',
'一键获取所有武器', '一键获取所有武器',
'一键添加所有宝石',
'设置武器等级', '设置武器等级',
'设置武器和技能', '设置武器和技能',
'清除指定背包文字', '清除指定背包文字',
@ -349,6 +350,15 @@ export class GM extends Component {
})(); })();
break; break;
} }
case '一键添加所有宝石': {
void (async () => {
const added = await gg.gem.gmGrantAllGems();
gg.data.saveToServer();
gg.ui.showToast(added > 0 ? `已再发宝石 ${added} 种` : 'Weapon 表无宝石');
GM.show();
})();
break;
}
case '设置武器和技能': case '设置武器和技能':
gg.ui.openPanel(UIID.PopGM, { data: 2 }); gg.ui.openPanel(UIID.PopGM, { data: 2 });
GM.show(); GM.show();
@ -753,6 +763,7 @@ export class GM extends Component {
· 今日首次登录:触发每日重置逻辑 · 今日首次登录:触发每日重置逻辑
· 复制存档:复制当前 doc JSON 到剪贴板 · 复制存档:复制当前 doc JSON 到剪贴板
· 一键获取所有武器:Weapon 全表成品各发 1 把(含 isshow=1 与新增;新武器保留弹窗/角标) · 一键获取所有武器:Weapon 全表成品各发 1 把(含 isshow=1 与新增;新武器保留弹窗/角标)
· 一键添加所有宝石:Weapon 全表每颗再发 1 颗(每次点击叠加一套)
· 设置武器等级 / 设置武器和技能:打开 PopGM 面板 · 设置武器等级 / 设置武器和技能:打开 PopGM 面板
· 清除指定背包文字:恢复随机 · 清除指定背包文字:恢复随机
· 快速胜利 / 关闭快速胜利:调试通关 · 快速胜利 / 关闭快速胜利:调试通关

@ -198,6 +198,21 @@ export class GemDataManager extends Singleton {
return true; return true;
} }
/** 是否有可用宝石达到合成门槛(free ≥ synthesisnumber 且有目标) */
canFuseAny(): boolean {
const all = this.getAllGemList();
for (let i = 0; i < all.length; i++) {
const conf = all[i];
const fromId = Math.floor(Number(conf?.id) || 0);
if (fromId <= 0) continue;
const need = Math.floor(Number(conf.synthesisnumber) || 0);
const targets = conf.synthesisitem ?? [];
if (need <= 0 || !targets.length) continue;
if (this.getFreeCount(fromId) >= need) return true;
}
return false;
}
/** /**
* 多轮一键合成;返回净增可用数 >0 的宝石。 * 多轮一键合成;返回净增可用数 >0 的宝石。
*/ */
@ -248,6 +263,28 @@ export class GemDataManager extends Singleton {
return list; return list;
} }
/**
* GM:Weapon 全表每颗宝石再发 1 颗(Item 库存)。每次点击叠加一套。
* @returns 本次发放的宝石种数
*/
async gmGrantAllGems(): Promise<number> {
await this.ensureTable();
const list = this.getAllGemList();
if (!list.length) {
console.warn(LOG, 'GM 一键发宝石:Weapon 表为空或未加载');
return 0;
}
let added = 0;
for (let i = 0; i < list.length; i++) {
const id = Math.floor(Number(list[i]?.id) || 0);
const itemId = Math.floor(Number(list[i]?.itemid) || 0);
if (id <= 0 || itemId <= 0) continue;
gg.data.addItemNum(itemId, 1);
added++;
}
return added;
}
/** 类型 + 专属上限(slot 上原宝石不计入专属占用) */ /** 类型 + 专属上限(slot 上原宝石不计入专属占用) */
private _canWearType(element: number, conf: ITableWeapon, slot: number): boolean { private _canWearType(element: number, conf: ITableWeapon, slot: number): boolean {
const t = Math.floor(Number(conf.type) || 0); const t = Math.floor(Number(conf.type) || 0);

@ -32,15 +32,7 @@ export class Auto_BattleWeaponDescHero extends UIBase {
lb_attckSpeed:Label; lb_attckSpeed:Label;
lb_skillName:Label; lb_skillName:Label;
lb_skillDesc:Label; lb_skillDesc:Label;
lb_weaponAtk:Label; ui_gemList:Node;
lb_weaponName:Label;
lb_weaponDesc:Label;
ui_progress:Node;
lb_progress:Label;
ui_type1:Node;
ui_type3:Node;
ui_type2:Node;
ui_type4:Node;
onLoad(){ onLoad(){
super.onLoad(); super.onLoad();
this.ui_bgMask = find("ui_bgMask",this.node); this.ui_bgMask = find("ui_bgMask",this.node);
@ -52,15 +44,7 @@ export class Auto_BattleWeaponDescHero extends UIBase {
this.lb_attckSpeed = find("弹窗主体/lb_attckSpeed",this.node)?.getComponent(Label); this.lb_attckSpeed = find("弹窗主体/lb_attckSpeed",this.node)?.getComponent(Label);
this.lb_skillName = find("弹窗主体/lb_skillName",this.node)?.getComponent(Label); this.lb_skillName = find("弹窗主体/lb_skillName",this.node)?.getComponent(Label);
this.lb_skillDesc = find("弹窗主体/lb_skillDesc",this.node)?.getComponent(Label); this.lb_skillDesc = find("弹窗主体/lb_skillDesc",this.node)?.getComponent(Label);
this.lb_weaponAtk = find("弹窗主体/lb_weaponAtk",this.node)?.getComponent(Label); this.ui_gemList = find("弹窗主体/ui_gemList",this.node);
this.lb_weaponName = find("弹窗主体/lb_weaponName",this.node)?.getComponent(Label);
this.lb_weaponDesc = find("弹窗主体/lb_weaponDesc",this.node)?.getComponent(Label);
this.ui_progress = find("弹窗主体/ui_progress",this.node);
this.lb_progress = find("弹窗主体/lb_progress",this.node)?.getComponent(Label);
this.ui_type1 = find("弹窗主体/ui_type1",this.node);
this.ui_type3 = find("弹窗主体/ui_type3",this.node);
this.ui_type2 = find("弹窗主体/ui_type2",this.node);
this.ui_type4 = find("弹窗主体/ui_type4",this.node);
} }
onEnable() { onEnable() {
} }

@ -2,26 +2,26 @@
//********************* //*********************
// create by 流云 // create by 流云
// time: Sat Sep 05 2026 // time: Sat Sep 05 2026
// desc: 战斗内点击组合字武将 — 详情弹窗 // desc: 战斗内点击五行数字 — 详情弹窗(数字皮 / 普攻技能 / 镶嵌宝石)
//********************* //*********************
import { import {
_decorator, _decorator,
Color,
EventTouch, EventTouch,
instantiate, instantiate,
Label, Label,
Layout,
Node, Node,
Prefab,
sp,
Sprite, Sprite,
Tween, Tween,
UITransform, UITransform,
UIOpacity, UIOpacity,
UIRenderer, UIRenderer,
Vec3, Vec3,
sp,
} from 'cc'; } from 'cc';
import { type IPopAnimConfig, type IPopAnimSession } from '../../../mx/module/ui/popAnim'; import { type IPopAnimConfig, type IPopAnimSession } from '../../../mx/module/ui/popAnim';
import { ensureSpineSkeletonDataRuntime } from 'db://assets/mx/module/lang/spineRuntimePatch'; import { ensureSpineSkeletonDataRuntime } from 'db://assets/mx/module/lang/spineRuntimePatch';
import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad';
import { BATTLE_EVENT_ALL, BattleEventType, type BattleEvent } from 'db://assets/mx/module/battle'; import { BATTLE_EVENT_ALL, BattleEventType, type BattleEvent } from 'db://assets/mx/module/battle';
import type { EntityId } from 'db://assets/mx/module/ecs'; import type { EntityId } from 'db://assets/mx/module/ecs';
import { GEvent } from '../../../mx/module/event/GEvent'; import { GEvent } from '../../../mx/module/event/GEvent';
@ -29,59 +29,37 @@ import { Auto_BattleWeaponDescHero } from './Auto_BattleWeaponDescHero';
import type { TileEntitySnapshot, TowerRuntimeSnapshot } from '../../battle/combat/TowerQueryTypes'; import type { TileEntitySnapshot, TowerRuntimeSnapshot } from '../../battle/combat/TowerQueryTypes';
import { TowerQueryService } from '../../battle/combat/TowerQueryService'; import { TowerQueryService } from '../../battle/combat/TowerQueryService';
import { deriveTowerSetupStats, TowerStatDisplay } from '../../battle/combat/TowerStatResolver'; import { deriveTowerSetupStats, TowerStatDisplay } from '../../battle/combat/TowerStatResolver';
import { getTowerExpHost, needTowerExpForLevel } from '../../battle/combat/TowerExpHost'; import { heroNormalSkillId, isElementHero } from '../../battle/config/BattleHeroTable';
import { resolveHeroBattleWeaponId } from '../../battle/combat/TowerLoadoutService'; import { BATTLE_HERO_NUM_PATH } from '../../battle/res/BattleHeroResCollector';
import { HeroSkin } from '../../battle/view/HeroSkin'; import { glyphSpineHasSkin } from '../../battle/view/GlyphSpineSkin';
import { Skill } from '../../battle/skill/Skill'; import { GBundle } from '../../game/ConfigRes';
import { heroUltSkillId } from '../../battle/config/BattleHeroTable';
import { applyWeaponSpineSkin } from '../../battle/view/GlyphSpineSkin';
import { BattleSpinePlayback } from '../../battle/presentation/BattleSpinePlayback';
import { GBundle, GPath } from '../../game/ConfigRes';
import { TableNames } from '../../game/ConfigTableData'; import { TableNames } from '../../game/ConfigTableData';
import type { Player } from '../../game/Player';
const { ccclass } = _decorator; const { ccclass } = _decorator;
const LOG = '[UIBattleWeaponDescHero]'; const LOG = '[UIBattleWeaponDescHero]';
/** 品质 → 武器名字体色 */ /** 空镶嵌提示文案(与预制体 gtemplate/lb 默认一致) */
const WEAPON_NAME_FONT_HEX: Record<number, string> = { const GEM_EMPTY_TIP = '尚未镶嵌宝石';
0: '#D8D8D8', const NUM_IDLE = '待机';
1: '#6FA86F',
2: '#6FA2A8', /** 五行数字详情弹窗入参 */
3: '#B390D9',
4: '#F9C948',
};
/** 品质 → 武器名描边色 */
const WEAPON_NAME_OUTLINE_HEX: Record<number, string> = {
0: '#4A4A4A',
1: '#386c42',
2: '#285c6d',
3: '#623073',
4: '#734930',
};
/** 组合武将详情弹窗入参 */
export interface BattleWeaponDescHeroPanelData { export interface BattleWeaponDescHeroPanelData {
entity: TileEntitySnapshot; entity: TileEntitySnapshot;
runtime?: TowerRuntimeSnapshot | null; runtime?: TowerRuntimeSnapshot | null;
/** 敌方阵营皮肤(组合字 皮肤字2_*) */ /** 敌方阵营(敌方不读本机镶嵌) */
enemyCamp?: boolean; enemyCamp?: boolean;
/** 已解析的展示武器 id(敌方优先 AI 档案) */ /** 已解析的展示武器/技能宝石 id(兼容旧入参) */
weaponId?: number; weaponId?: number;
weaponWorldPos?: { x: number; y: number; z?: number }; weaponWorldPos?: { x: number; y: number; z?: number };
gridStep?: number; gridStep?: number;
trySwitchAtUiPos?: (uiPos: { x: number; y: number }) => boolean; trySwitchAtUiPos?: (uiPos: { x: number; y: number }) => boolean;
/** 监视中的地图/背包格(切换选中格时随 prema 更新) */ /** 监视中的地图/背包格 */
watchTarget?: { kind: 'map' | 'bag'; index: number } | null; watchTarget?: { kind: 'map' | 'bag'; index: number } | null;
/** 已部署 ECS 实体(地图格实时刷新 fallback) */ /** 已部署 ECS 实体 */
watchEntityId?: EntityId; watchEntityId?: EntityId;
/** 从棋盘重新读取 entity/runtime(击杀加经验后刷新数值) */ /** 从棋盘重新读取 entity/runtime */
resolveLiveData?: () => Pick<BattleWeaponDescHeroPanelData, 'entity' | 'runtime' | 'weaponId'> | null; resolveLiveData?: () => Pick<BattleWeaponDescHeroPanelData, 'entity' | 'runtime' | 'weaponId'> | null;
} }
/** 经验条满宽(像素) */
const PROGRESS_MAX_WIDTH = 225;
const WEAPON_SPINE_IDLE = '待机';
@ccclass('UIBattleWeaponDescHero') @ccclass('UIBattleWeaponDescHero')
export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero { export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
private static readonly PANEL_GAP_GRIDS = 1.7; private static readonly PANEL_GAP_GRIDS = 1.7;
@ -89,11 +67,13 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
private _popupBody: Node | null = null; private _popupBody: Node | null = null;
/** 递增时取消尚未完成的关窗销毁 */ /** 递增时取消尚未完成的关窗销毁 */
private _panelLifeGen = 0; private _panelLifeGen = 0;
/** ui_node 下挂的武将展示节点 */ /** ui_node 下挂的数字预制体节点 */
private _heroNode: Node | null = null; private _numNode: Node | null = null;
/** 当前面板已解析的展示武器(defer 复用,避免二次解析失败把 ui_type 隐藏) */ /** 宝石列表模板 */
private _displayWeaponRow: ITableWeapon | null = null; private _gemTpl: Node | null = null;
/** 静态区(武将/技能/武器名等)缓存键;切换仅刷新数值区时不重建 */ /** 已克隆的宝石行 */
private readonly _gemRows: Node[] = [];
/** 静态区缓存键 */
private _staticViewKey = ''; private _staticViewKey = '';
/** 战斗事件监听是否已绑定 */ /** 战斗事件监听是否已绑定 */
private _battleEventBound = false; private _battleEventBound = false;
@ -103,6 +83,7 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
onLoad(): void { onLoad(): void {
super.onLoad(); super.onLoad();
this._popupBody = this.node.getChildByName('弹窗主体'); this._popupBody = this.node.getChildByName('弹窗主体');
this._cacheGemTemplate();
} }
onEnable(): void { onEnable(): void {
@ -116,10 +97,10 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
this.ui_bgMask.off(Node.EventType.TOUCH_END, this.click_ui_bgMask, this); this.ui_bgMask.off(Node.EventType.TOUCH_END, this.click_ui_bgMask, this);
} }
this._unbindLiveRefresh(); this._unbindLiveRefresh();
this.unschedule(this._deferRefreshWeaponType);
this.unschedule(this._deferredRefreshDynamic); this.unschedule(this._deferredRefreshDynamic);
this._staticViewKey = ''; this._staticViewKey = '';
this._clearHeroNode(); this._clearNumNode();
this._clearGemRows();
super.onDisable(); super.onDisable();
} }
@ -173,41 +154,40 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
this._panelLifeGen++; this._panelLifeGen++;
this._ensurePanelVisible(); this._ensurePanelVisible();
this._bindLiveRefresh(); this._bindLiveRefresh();
this._cacheGemTemplate();
const ctx = this._resolveRefreshContext(); const ctx = this._resolveRefreshContext();
if (!ctx) { if (!ctx) {
this._staticViewKey = ''; this._staticViewKey = '';
this._clearLabels(); this._clearLabels();
this._clearHeroNode(); this._clearNumNode();
this._hideWeaponTypes(); this._clearGemRows();
return; return;
} }
const { data, w1, level, levelLimit, stats, weaponRow, runtime, enemyCamp } = ctx; const { data, w1, level, levelLimit, stats, runtime, enemyCamp } = ctx;
const staticViewKey = this._buildStaticViewKey(w1.id, weaponRow?.id ?? 0, enemyCamp); const element = Math.floor(Number(w1.herotype) || 0);
const needStaticRefresh = staticViewKey !== this._staticViewKey || !this._heroNode?.isValid; const staticViewKey = this._buildStaticViewKey(w1.id, level, element, enemyCamp);
const needStaticRefresh = staticViewKey !== this._staticViewKey || !this._numNode?.isValid;
if (needStaticRefresh) { if (needStaticRefresh) {
this._staticViewKey = staticViewKey; this._staticViewKey = staticViewKey;
this._displayWeaponRow = weaponRow; this._refreshNumber(w1);
this._refreshHero(w1, level, enemyCamp); this._refreshStaticLabels(w1, level, levelLimit);
this._refreshStaticLabels(w1, level, levelLimit, weaponRow); this._refreshGemList(element, enemyCamp);
this._refreshWeaponType(weaponRow);
this._scheduleRefreshWeaponType(weaponRow);
} }
this._refreshDynamicStats(w1, level, levelLimit, stats, weaponRow, runtime); this._refreshDynamicStats(w1, level, stats);
this._applyPanelPosition(data); this._applyPanelPosition(data);
} }
/** 合并 prema 与 resolveLiveData,得到最新等级/属性 */ /** 合并 prema 与 resolveLiveData */
private _resolveRefreshContext(): { private _resolveRefreshContext(): {
data: BattleWeaponDescHeroPanelData; data: BattleWeaponDescHeroPanelData;
w1: ITableHero; w1: ITableHero;
level: number; level: number;
levelLimit: number; levelLimit: number;
stats: TowerRuntimeSnapshot['stats'] | undefined; stats: TowerRuntimeSnapshot['stats'] | undefined;
weaponRow: ITableWeapon | null;
runtime: TowerRuntimeSnapshot | null; runtime: TowerRuntimeSnapshot | null;
enemyCamp: boolean; enemyCamp: boolean;
} | null { } | null {
@ -228,6 +208,9 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
const w1 = data.entity.tower?.config ?? null; const w1 = data.entity.tower?.config ?? null;
if (!w1) return null; if (!w1) return null;
if (!isElementHero(w1)) {
console.warn(LOG, '非五行数字,详情仅支持五行', w1.id, w1.herotype);
}
let runtime = data.runtime ?? null; let runtime = data.runtime ?? null;
const watchEntityId = data.watchEntityId ?? runtime?.entityId ?? 0; const watchEntityId = data.watchEntityId ?? runtime?.entityId ?? 0;
@ -245,30 +228,24 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
: null; : null;
const stats = liveStats ?? runtime?.stats ?? data.entity.tower?.stats; const stats = liveStats ?? runtime?.stats ?? data.entity.tower?.stats;
const enemyCamp = !!data.enemyCamp; const enemyCamp = !!data.enemyCamp;
const weaponRow = this._resolveWeaponRow(w1.id, runtime, enemyCamp, data.weaponId ?? 0);
return { data, w1, level, levelLimit, stats, weaponRow, runtime, enemyCamp }; return { data, w1, level, levelLimit, stats, runtime, enemyCamp };
} }
/** 击杀/升级后仅刷新 lb_level 等数值区 */ /** 击杀/升级后仅刷新数值区 */
private _refreshDynamicFromLive(): void { private _refreshDynamicFromLive(): void {
if (!this.node?.isValid || !this.node.active) return; if (!this.node?.isValid || !this.node.active) return;
const ctx = this._resolveRefreshContext(); const ctx = this._resolveRefreshContext();
if (!ctx) return; if (!ctx) return;
const { w1, level, levelLimit, stats, weaponRow, runtime, enemyCamp } = ctx; const { w1, level, levelLimit, stats, enemyCamp } = ctx;
const staticViewKey = this._buildStaticViewKey(w1.id, weaponRow?.id ?? 0, enemyCamp); const element = Math.floor(Number(w1.herotype) || 0);
const staticViewKey = this._buildStaticViewKey(w1.id, level, element, enemyCamp);
if (staticViewKey !== this._staticViewKey) { if (staticViewKey !== this._staticViewKey) {
this.onInit(); this.onInit();
return; return;
} }
this._refreshDynamicStats(w1, level, levelLimit, stats, weaponRow, runtime); this._refreshDynamicStats(w1, level, stats);
} if (this.lb_lvMax) this.lb_lvMax.string = `满级${levelLimit}级`;
/** 入场动效结束后再刷一次武器 Spine(避免首帧节点未就绪) */
protected onPopOpenRevealEnd(): void {
if (this._displayWeaponRow) {
this._refreshWeaponType(this._displayWeaponRow);
}
} }
destroyPanel(tweenType = 1) { destroyPanel(tweenType = 1) {
@ -303,135 +280,117 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
this.close(2); this.close(2);
} }
/** ui_node:heroTemplate + HeroSkin 播待机 */ /** 静态区缓存键 */
private _refreshHero(w1: ITableHero, level: number, enemyCamp = false): void { private _buildStaticViewKey(heroId: number, level: number, element: number, enemyCamp: boolean): string {
return `${Math.floor(Number(heroId) || 0)}|${Math.floor(Number(level) || 0)}|${element}|${enemyCamp ? 1 : 0}`;
}
/**
* ui_node:按 Hero.number 加载数字预制体,并切五行皮肤。
* @param w1 当前数字对应 Hero
*/
private _refreshNumber(w1: ITableHero): void {
if (!this.ui_node?.isValid) return; if (!this.ui_node?.isValid) return;
this._clearHeroNode(); this._clearNumNode();
const prefab = gg.res.getRes<Prefab>('heroTemplate', GPath.Prefab, GBundle.Heros, Prefab); const num = Math.floor(Number(w1.number) || 0);
if (!prefab) { if (!(num > 0)) {
console.log('[UIBattleWeaponDescHero] heroTemplate 预制体缺失', w1.id); console.warn(LOG, 'Hero.number 无效', w1.id, w1.number);
this.ui_node.active = false;
return;
}
const skinName = this._elementSkinName(Math.floor(Number(w1.herotype) || 0));
if (!skinName) {
console.warn(LOG, '五行皮肤未定义', w1.id, w1.herotype);
this.ui_node.active = false; this.ui_node.active = false;
return; return;
} }
const heroNode = instantiate(prefab); let node: Node | null = null;
heroNode.parent = this.ui_node; try {
heroNode.setPosition(0, 0, 0); node = gg.res.getNode(BATTLE_HERO_NUM_PATH, String(num), GBundle.BattleHero);
heroNode.setScale(1, 1, 1); } catch (e) {
this._heroNode = heroNode; console.warn(LOG, '数字预制体加载失败', num, String(e));
this.ui_node.active = true; this.ui_node.active = false;
return;
const skill = heroNode.getComponent(Skill); }
if (skill) skill.enabled = false; if (!node?.isValid) {
console.warn(LOG, '数字预制体缺失', num);
const heroSkin = heroNode.getComponent(HeroSkin); this.ui_node.active = false;
if (!heroSkin) {
console.log('[UIBattleWeaponDescHero] HeroSkin 缺失', w1.id);
return; return;
} }
heroSkin.setQualityFrameVisible(false);
heroSkin.setEnemyCamp(enemyCamp);
heroSkin.setData(w1);
heroSkin.setSkinByLevelAndQuality(level, enemyCamp);
heroSkin.playIdleAnim();
}
private _clearHeroNode(): void {
if (this._heroNode?.isValid) {
const heroSkin = this._heroNode.getComponent(HeroSkin);
heroSkin?.releaseDisplay();
this._heroNode.destroy();
}
this._heroNode = null;
if (this.ui_node?.isValid) {
this.ui_node.removeAllChildren();
}
}
/** 可展示的武器配表行(Weapon 表且 type 1~4) */
private _tryDisplayWeaponRow(weaponId: number): ITableWeapon | null {
const id = Math.floor(Number(weaponId) || 0);
if (id <= 0) return null;
const row = gg.equip.getEquip(id)
?? gg.data.table.getTableData<ITableWeapon>(TableNames.Weapon, id);
if (!row) return null;
const type = Number(row.type) | 0;
if (type < 1 || type > 4) return null;
return row;
}
private _resolveWeaponRow(
heroId: number,
runtime?: TowerRuntimeSnapshot | null,
enemyCamp = false,
presetWeaponId = 0,
): ITableWeapon | null {
const fromPreset = this._tryDisplayWeaponRow(presetWeaponId);
if (fromPreset) return fromPreset;
const loadout = runtime?.loadout;
if (loadout && loadout.weapon1Id === heroId) {
const fromRuntime = this._tryDisplayWeaponRow(loadout.weaponId ?? 0);
if (fromRuntime) return fromRuntime;
}
const fallbackId = enemyCamp
? resolveHeroBattleWeaponId(heroId, this._resolveEnemyPlayer())
: gg.equip.getHeroDisplayWeaponId(heroId);
const fromFallback = this._tryDisplayWeaponRow(fallbackId);
if (fromFallback) return fromFallback;
console.log(
LOG,
enemyCamp ? '敌方武器 id 缺失' : '武器 id 缺失',
heroId,
'preset=',
presetWeaponId,
'runtime=',
loadout?.weaponId ?? 0,
'fallback=',
fallbackId,
);
return null;
}
/** 弹窗入场后再刷一次武器 Spine(使用 onInit 缓存,避免二次解析失败清空 ui_type) */ node.parent = this.ui_node;
private _deferRefreshWeaponType = (): void => { node.layer = this.ui_node.layer;
if (!this.node?.isValid) return; node.setPosition(0, 0, 0);
const weaponRow = this._displayWeaponRow; node.setScale(1, 1, 1);
if (!weaponRow) return; node.active = true;
this._refreshWeaponType(weaponRow); this._numNode = node;
}; this.ui_node.active = true;
this._playElementSkin(node, skinName, w1.id);
}
/** 延后刷新武器 Spine(本帧末 + 下一帧各一次) */ /** Hero.herotype → 数字 Spine 皮肤名 */
private _scheduleRefreshWeaponType(weaponRow: ITableWeapon | null): void { private _elementSkinName(herotype: number): string {
this.unschedule(this._deferRefreshWeaponType); switch (herotype) {
if (!weaponRow) return; case 1: return '金';
this.scheduleOnce(this._deferRefreshWeaponType, 0); case 2: return '木';
this.scheduleOnce(this._deferRefreshWeaponType, 0.05); case 3: return '水';
case 4: return '火';
case 5: return '土';
default: return '';
}
} }
/** 敌方 Player(与 TowerLoadout 解析同源) */ /**
private _resolveEnemyPlayer(): Player | null { * 数字节点切五行皮并播待机。
const battle = gg.game.CurentBattle; * @param node 数字预制体
if (!battle) return null; * @param skinName 金木水火土
const localPid = battle.player?.playerId ?? 0; * @param heroId 日志用
return battle.getPlayer(localPid === 0 ? 1 : 0) ?? null; */
private _playElementSkin(node: Node, skinName: string, heroId: number): void {
const sk = node.getComponent(sp.Skeleton);
if (!sk?.isValid) {
console.warn(LOG, '数字节点缺少 Skeleton', node.name, heroId);
return;
}
const sd = sk.skeletonData;
if (!sd?.isValid) {
sk.enabled = false;
console.warn(LOG, '数字节点 skeletonData 无效', node.name, heroId);
return;
}
sk.enabled = true;
sk.setAnimationCacheMode(sp.SpineAnimationCacheMode.REALTIME);
ensureSpineSkeletonDataRuntime(sd);
if (!glyphSpineHasSkin(sk, skinName)) {
console.warn(LOG, '数字皮不存在', skinName, node.name, heroId);
return;
}
sk.setSkin(skinName);
sk.setSlotsToSetupPose();
const runtime = sd.getRuntimeData?.(true);
if (runtime?.findAnimation?.(NUM_IDLE)) {
sk.setAnimation(0, NUM_IDLE, true);
}
} }
/** 静态区缓存键:同武将+武器+阵营切换时仅刷新数值 */ /** 归还数字预制体 */
private _buildStaticViewKey(heroId: number, weaponId: number, enemyCamp: boolean): string { private _clearNumNode(): void {
return `${Math.floor(Number(heroId) || 0)}|${Math.floor(Number(weaponId) || 0)}|${enemyCamp ? 1 : 0}`; const held = this._numNode;
this._numNode = null;
if (!held?.isValid) {
if (this.ui_node?.isValid) this.ui_node.removeAllChildren();
return;
}
Tween.stopAllByTarget(held);
if (held['resKey']) gg.res.putNode(held);
else held.destroy();
if (this.ui_node?.isValid) this.ui_node.removeAllChildren();
} }
/** 武将名/满级/技能/武器文案与 Spine(打开或切换武将时刷新) */ /** 名称 / 满级 / 普攻技能文案 */
private _refreshStaticLabels( private _refreshStaticLabels(w1: ITableHero, level: number, levelLimit: number): void {
w1: ITableHero,
level: number,
levelLimit: number,
weaponRow: ITableWeapon | null,
): void {
if (this.lb_name) { if (this.lb_name) {
this.lb_name.string = gg.lang.get(w1.name); this.lb_name.string = gg.lang.get(w1.name);
} }
@ -439,37 +398,43 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
this.lb_lvMax.string = `满级${levelLimit}级`; this.lb_lvMax.string = `满级${levelLimit}级`;
} }
this._refreshSkillLabels(w1, level); this._refreshSkillLabels(w1, level);
}
if (!weaponRow) { /**
if (this.lb_weaponName) { * lb_skillName / lb_skillDesc:读 Hero.atk_id 对应 Skill。
this.lb_weaponName.string = ''; * Skill.name 配表常为空时,名称回退为 desc,避免「技能:」右侧空白。
this._resetWeaponNameStyle(); * @param w1 Hero
} * @param level 合成等级
if (this.lb_weaponDesc) this.lb_weaponDesc.string = ''; */
return; private _refreshSkillLabels(w1: ITableHero, level: number): void {
const skillId = heroNormalSkillId(w1, level);
const skill = skillId > 0
? gg.data.table.getTableData<ITableSkill>(TableNames.Skill, skillId)
: null;
if (skillId > 0 && !skill) {
console.warn(LOG, 'Skill 缺失', skillId, w1.id, level);
} }
const nameKey = skill ? String(skill.name ?? '').trim() : '';
if (this.lb_weaponName) { const descKey = skill ? String(skill.desc ?? '').trim() : '';
this.lb_weaponName.string = gg.lang.get(weaponRow.name); if (skill && !nameKey) {
this._applyWeaponNameStyle(weaponRow.quality); console.warn(LOG, 'Skill.name 为空,名称回退 desc', skillId, w1.id);
} }
if (this.lb_weaponDesc) { if (this.lb_skillName) {
this.lb_weaponDesc.string = gg.lang.get(weaponRow.desc); const nameText = nameKey ? gg.lang.get(nameKey) : (descKey ? gg.lang.get(descKey) : '');
this.lb_skillName.string = nameText;
}
if (this.lb_skillDesc) {
this.lb_skillDesc.string = descKey ? gg.lang.get(descKey) : '';
} }
} }
/** /** 等级 / 攻击 / 攻速 */
* 实时数值:等级、攻击、攻速、武器攻击、经验条(每次打开/切换格子都读最新 runtime)
*/
private _refreshDynamicStats( private _refreshDynamicStats(
w1: ITableHero, w1: ITableHero,
level: number, level: number,
levelLimit: number,
stats: TowerRuntimeSnapshot['stats'] | undefined, stats: TowerRuntimeSnapshot['stats'] | undefined,
weaponRow: ITableWeapon | null,
runtime?: TowerRuntimeSnapshot | null,
): void { ): void {
const setup = deriveTowerSetupStats(w1, level, weaponRow?.id); const setup = deriveTowerSetupStats(w1, level, 0);
const attack = stats?.attack ?? setup.attack; const attack = stats?.attack ?? setup.attack;
const attackSpeed = stats?.attackSpeed ?? setup.attackSpeed; const attackSpeed = stats?.attackSpeed ?? setup.attackSpeed;
@ -482,170 +447,157 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
if (this.lb_attckSpeed) { if (this.lb_attckSpeed) {
this.lb_attckSpeed.string = TowerStatDisplay.attackSpeed(attackSpeed); this.lb_attckSpeed.string = TowerStatDisplay.attackSpeed(attackSpeed);
} }
if (this.lb_weaponAtk) {
if (!weaponRow) {
this.lb_weaponAtk.string = '';
} else {
const exclusiveMul = gg.equip.getEquipExclusiveMul(w1.id, weaponRow);
const weaponAtk = Math.floor((weaponRow.atk ?? 0) * exclusiveMul);
this.lb_weaponAtk.string = `${weaponAtk}`;
}
}
this._refreshExpProgress(w1, level, levelLimit, runtime);
} }
/** 刷新当前等级武将技能名与描述 */ /** 缓存 gtemplate */
private _refreshSkillLabels(w1: ITableHero, level: number): void { private _cacheGemTemplate(): void {
const skillId = heroUltSkillId(w1, level); if (this._gemTpl?.isValid) return;
const skill = skillId > 0 const list = this.ui_gemList;
? gg.data.table.getTableData<ITableSkill>(TableNames.Skill, skillId) if (!list?.isValid) return;
: null; const tpl = list.getChildByName('gtemplate');
if (skillId > 0 && !skill) { if (!tpl?.isValid) {
console.log(LOG, 'Skill 缺失', skillId, w1.id, level); console.warn(LOG, 'ui_gemList/gtemplate 缺失');
}
if (this.lb_skillName) {
this.lb_skillName.string = skill ? gg.lang.get(skill.name) : '';
}
if (this.lb_skillDesc) {
this.lb_skillDesc.string = skill ? gg.lang.get(skill.desc) : '';
}
}
/** 按武器品质设置武器名字体色与描边 */
private _applyWeaponNameStyle(quality: number): void {
const label = this.lb_weaponName;
if (!label) return;
const q = Math.floor(Number(quality) || 0);
const fontHex = WEAPON_NAME_FONT_HEX[q];
const outlineHex = WEAPON_NAME_OUTLINE_HEX[q];
if (!fontHex || !outlineHex) {
console.log(LOG, '未知武器品质色', quality);
this._resetWeaponNameStyle();
return; return;
} }
label.color = Color.fromHEX(new Color(), fontHex); this._gemTpl = tpl;
label.enableOutline = true; tpl.active = false;
label.outlineColor = Color.fromHEX(new Color(), outlineHex);
}
/** 重置武器名样式 */
private _resetWeaponNameStyle(): void {
const label = this.lb_weaponName;
if (!label) return;
label.color = Color.WHITE;
label.enableOutline = false;
}
/** 从弹窗主体取 ui_type 节点(不依赖 Auto 缓存引用) */
private _getWeaponTypeNode(weaponType: number): Node | null {
const body = this._popupBody ?? this.node.getChildByName('弹窗主体');
if (!body?.isValid) return null;
const t = Math.floor(Number(weaponType) || 0);
if (t < 1 || t > 4) return null;
return body.getChildByName(`ui_type${t}`);
}
/** 同步 Auto 节点引用(供外部工具链;展示以 _getWeaponTypeNode 为准) */
private _ensureWeaponTypeNodes(): void {
if (!this.ui_type1?.isValid) this.ui_type1 = this._getWeaponTypeNode(1);
if (!this.ui_type2?.isValid) this.ui_type2 = this._getWeaponTypeNode(2);
if (!this.ui_type3?.isValid) this.ui_type3 = this._getWeaponTypeNode(3);
if (!this.ui_type4?.isValid) this.ui_type4 = this._getWeaponTypeNode(4);
} }
/** ui_type1~4:按武器 type 显隐并切肤 */ /**
private _refreshWeaponType(weaponRow: ITableWeapon | null): void { * 固定刷新 4 个镶嵌槽:有宝石画完整行,空槽只显示 lb。
this._ensureWeaponTypeNodes(); * @param element Hero.herotype 1~5
this._hideWeaponTypes(); * @param enemyCamp 敌方不读本机镶嵌(四槽皆空)
if (!weaponRow) return; */
private _refreshGemList(element: number, enemyCamp: boolean): void {
const weaponType = Number(weaponRow.type) | 0; this._cacheGemTemplate();
const skin = String(weaponRow.skin ?? '').trim(); this._clearGemRows();
if (weaponType < 1 || weaponType > 4) { const list = this.ui_gemList;
console.log(LOG, '武器 type 无效', weaponRow.id, weaponType); const tpl = this._gemTpl;
return; if (!list?.isValid || !tpl?.isValid) return;
}
if (!skin) { const slots = [0, 0, 0, 0];
console.log(LOG, '武器 skin 缺失', weaponRow.id); if (!enemyCamp && element >= 1 && element <= 5) {
try {
const raw = gg.gem.getElementSlots(element);
for (let i = 0; i < 4; i++) {
slots[i] = Math.floor(Number(raw[i]) || 0);
}
} catch (err) {
console.warn(LOG, 'getElementSlots 失败', element, err);
}
} }
const node = this._getWeaponTypeNode(weaponType); for (let i = 0; i < 4; i++) {
if (!node?.isValid) { const row = instantiate(tpl);
console.log(LOG, '武器类型节点缺失', weaponType, weaponRow.id); row.parent = list;
return; row.active = true;
const gemId = slots[i];
if (gemId > 0) {
const conf = gg.gem.getGem(gemId);
if (!conf) {
console.warn(LOG, '镶嵌宝石配表缺失', gemId, 'element', element, 'slot', i);
this._paintEmptyGemSlot(row);
} else {
this._paintGemRow(row, conf);
}
} else {
this._paintEmptyGemSlot(row);
}
this._gemRows.push(row);
} }
this._updateGemLayout(list);
node.active = true;
node.setScale(1, 1, 1);
this._applyWeaponSpine(node, skin, weaponRow.id);
} }
private _hideWeaponTypes(): void { /** 填充一颗已镶嵌宝石行 */
for (let t = 1; t <= 4; t++) { private _paintGemRow(row: Node, conf: ITableWeapon): void {
const node = this._getWeaponTypeNode(t); const lbEmpty = row.getChildByName('lb');
if (node?.isValid) node.active = false; if (lbEmpty?.isValid) lbEmpty.active = false;
}
}
private _applyWeaponSpine(typeNode: Node, skin: string, weaponId: number): void { const quality = Math.floor(Number(conf.quality) || 0);
typeNode.active = true; const lblv = row.getChildByName('lblv')?.getComponent(Label);
const skel = typeNode.getComponent(sp.Skeleton) ?? typeNode.getComponentInChildren(sp.Skeleton); if (lblv) {
if (!skel?.isValid) { lblv.node.active = true;
console.log(LOG, '武器 Spine 缺失', typeNode.name, weaponId); lblv.string = `Lv.${quality}`;
return;
} }
if (skel.node?.isValid) skel.node.active = true; const icon = row.getChildByName('icon');
skel.enabled = true; if (icon?.isValid) {
if (skel.skeletonData) ensureSpineSkeletonDataRuntime(skel.skeletonData); icon.active = true;
const iconName = String(conf.icon || '').trim();
const resolvedSkin = applyWeaponSpineSkin(skel, skin, LOG); if (!iconName) console.warn(LOG, '宝石 icon 缺失', conf.id);
if (skin && !resolvedSkin) { this._setItemSprite(icon, iconName ? `weapon/${iconName}` : '');
console.log(LOG, '武器 skin 不可用', weaponId, skin);
} }
const lbName = row.getChildByName('lbName')?.getComponent(Label);
BattleSpinePlayback.resetTracks(skel); if (lbName) {
try { lbName.node.active = true;
skel.setAnimation(0, WEAPON_SPINE_IDLE, true); lbName.string = this._gemBaseName(conf);
} catch (e) {
console.log(LOG, 'setAnimation 失败', WEAPON_SPINE_IDLE, typeNode.name, weaponId, String(e));
} }
const lbDesc = row.getChildByName('lbDesc')?.getComponent(Label);
const renderers = typeNode.getComponentsInChildren(UIRenderer); if (lbDesc) {
for (let i = 0; i < renderers.length; i++) { lbDesc.node.active = true;
const r = renderers[i]; lbDesc.string = gg.lang.get(conf.desc);
if (r?.isValid) r.enabled = true;
} }
const op = typeNode.getComponent(UIOpacity) ?? typeNode.getComponentInChildren(UIOpacity);
if (op?.isValid) op.opacity = 255;
} }
private _refreshExpProgress( /**
w1: ITableHero, * 宝石展示名:去掉配表名尾部「N级」,等级改由 lblv 的 Lv.N 展示。
level: number, * @param conf Weapon 行
levelLimit: number, */
runtime?: TowerRuntimeSnapshot | null, private _gemBaseName(conf: ITableWeapon): string {
): void { const raw = gg.lang.get(conf.name);
const progressUt = this.ui_progress?.getComponent(UITransform); const stripped = String(raw).replace(/\d+级\s*$/, '').trim();
if (!progressUt?.isValid) return; return stripped || raw;
}
if (level >= levelLimit) { /**
progressUt.setContentSize(PROGRESS_MAX_WIDTH, progressUt.contentSize.height); * 空槽:只显示 lb,文案固定「尚未镶嵌宝石」。
if (this.lb_progress) this.lb_progress.string = '满级'; * @param row 槽位行
if (this.ui_progress) this.ui_progress.active = true; */
private _paintEmptyGemSlot(row: Node): void {
const lbEmpty = row.getChildByName('lb')?.getComponent(Label);
if (lbEmpty) {
lbEmpty.node.active = true;
lbEmpty.string = GEM_EMPTY_TIP;
}
const lblv = row.getChildByName('lblv');
if (lblv?.isValid) lblv.active = false;
const icon = row.getChildByName('icon');
if (icon?.isValid) {
icon.active = false;
this._setItemSprite(icon, '');
}
const lbName = row.getChildByName('lbName');
if (lbName?.isValid) lbName.active = false;
const lbDesc = row.getChildByName('lbDesc');
if (lbDesc?.isValid) lbDesc.active = false;
}
/** Items bundle 图集 */
private _setItemSprite(node: Node | null, path: string): void {
if (!node?.isValid) return;
if (!path) {
node.getComponent(SpriteLoad)?.clear();
const spComp = node.getComponent(Sprite);
if (spComp) spComp.spriteFrame = null;
return; return;
} }
if (!node.getComponent(Sprite)) node.addComponent(Sprite);
node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items);
}
const need = needTowerExpForLevel(w1, level); /** 清理宝石克隆行(保留模板) */
const curRaw = runtime?.entityId ? getTowerExpHost().getExp(runtime.entityId) : 0; private _clearGemRows(): void {
const ratio = need > 0 ? Math.min(1, Math.max(0, curRaw / need)) : 0; for (let i = 0; i < this._gemRows.length; i++) {
progressUt.setContentSize(PROGRESS_MAX_WIDTH * ratio, progressUt.contentSize.height); const n = this._gemRows[i];
if (n?.isValid) n.destroy();
if (this.lb_progress) {
const curText = TowerStatDisplay.oneDecimal(curRaw);
const needText = TowerStatDisplay.oneDecimal(need);
this.lb_progress.string = need > 0 ? `${curText}/${needText}` : `${curText}/0`;
} }
if (this.ui_progress) this.ui_progress.active = true; this._gemRows.length = 0;
if (this._gemTpl?.isValid) this._gemTpl.active = false;
}
/** 触发布局 */
private _updateGemLayout(list: Node): void {
const lay = list.getComponent(Layout);
if (lay) lay.updateLayout();
} }
private _clearLabels(): void { private _clearLabels(): void {
@ -654,19 +606,8 @@ export class UIBattleWeaponDescHero extends Auto_BattleWeaponDescHero {
if (this.lb_lvMax) this.lb_lvMax.string = ''; if (this.lb_lvMax) this.lb_lvMax.string = '';
if (this.lb_attck) this.lb_attck.string = ''; if (this.lb_attck) this.lb_attck.string = '';
if (this.lb_attckSpeed) this.lb_attckSpeed.string = ''; if (this.lb_attckSpeed) this.lb_attckSpeed.string = '';
if (this.lb_weaponName) {
this.lb_weaponName.string = '';
this._resetWeaponNameStyle();
}
if (this.lb_weaponAtk) this.lb_weaponAtk.string = '';
if (this.lb_weaponDesc) this.lb_weaponDesc.string = '';
if (this.lb_skillName) this.lb_skillName.string = ''; if (this.lb_skillName) this.lb_skillName.string = '';
if (this.lb_skillDesc) this.lb_skillDesc.string = ''; if (this.lb_skillDesc) this.lb_skillDesc.string = '';
if (this.lb_progress) this.lb_progress.string = '';
if (this.ui_progress?.isValid) {
const ut = this.ui_progress.getComponent(UITransform);
if (ut) ut.setContentSize(0, ut.contentSize.height);
}
} }
private _applyPanelPosition(data: BattleWeaponDescHeroPanelData | null): void { private _applyPanelPosition(data: BattleWeaponDescHeroPanelData | null): void {

@ -29,7 +29,6 @@ export class Auto_PanelGemstone extends UIBase {
ui_content:Node; ui_content:Node;
ui_tagBtnBg:Node; ui_tagBtnBg:Node;
ui_tagBtns:Node; ui_tagBtns:Node;
btnAll:Node;
btn0:Node; btn0:Node;
btn1:Node; btn1:Node;
btn2:Node; btn2:Node;
@ -42,10 +41,9 @@ export class Auto_PanelGemstone extends UIBase {
this.ui_center = find("ui_center",this.node); this.ui_center = find("ui_center",this.node);
this.ui_list = find("ui_center/ui_list",this.node); this.ui_list = find("ui_center/ui_list",this.node);
this.btnClose = find("ui_center/btnClose",this.node); this.btnClose = find("ui_center/btnClose",this.node);
this.ui_content = find("ui_center/ScrollView/view/ui_content",this.node); this.ui_content = find("ui_center/ui_content",this.node);
this.ui_tagBtnBg = find("ui_center/tags/ui_tagBtnBg",this.node); this.ui_tagBtnBg = find("ui_center/tags/ui_tagBtnBg",this.node);
this.ui_tagBtns = find("ui_center/tags/ui_tagBtns",this.node); this.ui_tagBtns = find("ui_center/tags/ui_tagBtns",this.node);
this.btnAll = find("ui_center/tags/ui_tagBtns/btnAll",this.node);
this.btn0 = find("ui_center/tags/ui_tagBtns/btn0",this.node); this.btn0 = find("ui_center/tags/ui_tagBtns/btn0",this.node);
this.btn1 = find("ui_center/tags/ui_tagBtns/btn1",this.node); this.btn1 = find("ui_center/tags/ui_tagBtns/btn1",this.node);
this.btn2 = find("ui_center/tags/ui_tagBtns/btn2",this.node); this.btn2 = find("ui_center/tags/ui_tagBtns/btn2",this.node);
@ -56,7 +54,6 @@ export class Auto_PanelGemstone extends UIBase {
} }
onEnable() { onEnable() {
this.btnClose.on(Node.EventType.TOUCH_END, this.click_btnClose, this); this.btnClose.on(Node.EventType.TOUCH_END, this.click_btnClose, this);
this.btnAll.on(Node.EventType.TOUCH_END, this.click_btnAll, this);
this.btn0.on(Node.EventType.TOUCH_END, this.click_btn0, this); this.btn0.on(Node.EventType.TOUCH_END, this.click_btn0, this);
this.btn1.on(Node.EventType.TOUCH_END, this.click_btn1, this); this.btn1.on(Node.EventType.TOUCH_END, this.click_btn1, this);
this.btn2.on(Node.EventType.TOUCH_END, this.click_btn2, this); this.btn2.on(Node.EventType.TOUCH_END, this.click_btn2, this);
@ -67,7 +64,6 @@ export class Auto_PanelGemstone extends UIBase {
} }
onDisable() { onDisable() {
this.btnClose.off(Node.EventType.TOUCH_END, this.click_btnClose, this); this.btnClose.off(Node.EventType.TOUCH_END, this.click_btnClose, this);
this.btnAll.off(Node.EventType.TOUCH_END, this.click_btnAll, this);
this.btn0.off(Node.EventType.TOUCH_END, this.click_btn0, this); this.btn0.off(Node.EventType.TOUCH_END, this.click_btn0, this);
this.btn1.off(Node.EventType.TOUCH_END, this.click_btn1, this); this.btn1.off(Node.EventType.TOUCH_END, this.click_btn1, this);
this.btn2.off(Node.EventType.TOUCH_END, this.click_btn2, this); this.btn2.off(Node.EventType.TOUCH_END, this.click_btn2, this);
@ -77,7 +73,6 @@ export class Auto_PanelGemstone extends UIBase {
this.btnFuse.off(Node.EventType.TOUCH_END, this.click_btnFuse, this); this.btnFuse.off(Node.EventType.TOUCH_END, this.click_btnFuse, this);
} }
click_btnClose() {} click_btnClose() {}
click_btnAll() {}
click_btn0() {} click_btn0() {}
click_btn1() {} click_btn1() {}
click_btn2() {} click_btn2() {}

@ -2,26 +2,33 @@
//********************* //*********************
// create by 流云 // create by 流云
// time: Wed Oct 07 2026 // time: Wed Oct 07 2026
// desc: 宝石镶嵌 / 仓库 / 一键合成 // desc: 宝石镶嵌 / 页签仓库 / 一键合成
//********************* //*********************
import { _decorator, EventMouse, EventTouch, Input, input, instantiate, Node, ScrollView, sp, Sprite, UIOpacity, UITransform, Vec2, Vec3 } from 'cc'; import { _decorator, EventMouse, EventTouch, Input, input, instantiate, Node, sp, Sprite, UIOpacity, UITransform, Vec2, Vec3 } from 'cc';
import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad'; import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad';
import { ensureSpineSkeletonDataRuntime } from 'db://assets/mx/module/lang/spineRuntimePatch'; import { ensureSpineSkeletonDataRuntime } from 'db://assets/mx/module/lang/spineRuntimePatch';
import { GemGrid } from '../../com/GemGrid'; import { GemGrid } from '../../com/GemGrid';
import { FrameFlowHost } from '../../game/FrameFlowHost';
import { GBundle, UIID } from '../../game/ConfigRes'; import { GBundle, UIID } from '../../game/ConfigRes';
import { GEM_SLOT_COUNT, IGemBagEntry } from '../../game/GameDataEnums'; import { GEM_SLOT_COUNT, IGemBagEntry } from '../../game/GameDataEnums';
import { Auto_PanelGemstone } from './Auto_PanelGemstone'; import { Auto_PanelGemstone } from './Auto_PanelGemstone';
const { ccclass } = _decorator; const { ccclass } = _decorator;
const LOG = '[PanelGemstone]'; const LOG = '[PanelGemstone]';
/** 按下后移动超过此值开始拖 */
const DRAG_THRESHOLD = 18; const DRAG_THRESHOLD = 18;
const GHOST_OPACITY = 180; const GHOST_OPACITY = 180;
const ELEMENT_COUNT = 5; const ELEMENT_COUNT = 5;
const SLOT_NAMES = ['g1', 'g2', 'g3', 'g4']; const SLOT_NAMES = ['g1', 'g2', 'g3', 'g4'];
const ELEMENT_SKINS = ['', '金', '木', '水', '火', '土']; const ELEMENT_SKINS = ['', '金', '木', '水', '火', '土'];
/** 仓库每帧实例化数量 */
const BAG_PER_FRAME = 6;
/** 默认页签:通用 Weapon.type=1 */
const DEFAULT_FILTER = 1;
/** /**
* 宝石页:五行镶嵌 + 仓库筛选 + 拖拽装备/卸下 + 一键合成。 * 宝石页:五行镶嵌 + 页签仓库(无滚动)+ 拖拽镶嵌/卸下 + 一键合成。
* 页签 btn0~btn5 → type 1~6(通用/金/木/水/火/土),默认通用。
*/ */
@ccclass('UIPanelGemstone') @ccclass('UIPanelGemstone')
export class UIPanelGemstone extends Auto_PanelGemstone { export class UIPanelGemstone extends Auto_PanelGemstone {
@ -34,12 +41,16 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
private _elementItems: Node[] = []; private _elementItems: Node[] = [];
/** 仓库动态格 */ /** 仓库动态格 */
private _bagItems: Node[] = []; private _bagItems: Node[] = [];
/** 当前筛选:0全部 1通用 2~6五行 */ /** 当前筛选:1 通用;2~6 金木水火土 */
private _filterType = 0; private _filterType = DEFAULT_FILTER;
/** 仓库数据 */ /** 仓库数据 */
private _bagList: IGemBagEntry[] = []; private _bagList: IGemBagEntry[] = [];
/** ScrollView 节点(卸下命中区) */ /** 仓库分帧 */
private _scrollNode: Node | null = null; private _flow: FrameFlowHost | null = null;
/** 仓库填充代数 */
private _bagToken = 0;
/** 已分帧填充下标 */
private _bagFillIndex = 0;
private _pressing = false; private _pressing = false;
private _dragging = false; private _dragging = false;
@ -58,14 +69,9 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
onLoad(): void { onLoad(): void {
super.onLoad(); super.onLoad();
this._itemTpl = this.ui_list?.getChildByName('itemTemplate') ?? null; this._itemTpl = this.ui_list?.getChildByName('itemTemplate') ?? null;
if (this._itemTpl) { if (this._itemTpl) this._itemTpl.active = false;
this._itemTpl.active = false;
}
this._bagTpl = this.ui_content?.getChildByName('bagItemTemplate') ?? null; this._bagTpl = this.ui_content?.getChildByName('bagItemTemplate') ?? null;
if (this._bagTpl) { if (this._bagTpl) this._bagTpl.active = false;
this._bagTpl.active = false;
}
this._scrollNode = this.ui_content?.parent?.parent ?? null;
this._bindDrag(true); this._bindDrag(true);
} }
@ -77,12 +83,13 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
onDisable(): void { onDisable(): void {
this._bindDrag(false); this._bindDrag(false);
this._endDrag(); this._endDrag();
this._stopBagFill();
super.onDisable(); super.onDisable();
} }
onInit(): void { onInit(): void {
this._filterType = 0; this._filterType = DEFAULT_FILTER;
this._selectTag(this.btnAll); this._selectTag(this.btn0);
void this._refresh(); void this._refresh();
} }
@ -90,7 +97,6 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
void this.close(2); void this.close(2);
} }
click_btnAll(): void { this._onTag(0, this.btnAll); }
click_btn0(): void { this._onTag(1, this.btn0); } click_btn0(): void { this._onTag(1, this.btn0); }
click_btn1(): void { this._onTag(2, this.btn1); } click_btn1(): void { this._onTag(2, this.btn1); }
click_btn2(): void { this._onTag(3, this.btn2); } click_btn2(): void { this._onTag(3, this.btn2); }
@ -100,7 +106,7 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
click_btnFuse(): void { click_btnFuse(): void {
const gains = gg.gem.fuseAll(); const gains = gg.gem.fuseAll();
this._refreshBag(); this._rebuildBag();
this._refreshAllSlots(); this._refreshAllSlots();
if (!gains.length) { if (!gains.length) {
gg.ui.showToast('没有可合成的宝石'); gg.ui.showToast('没有可合成的宝石');
@ -109,11 +115,12 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
void gg.ui.openPanel(UIID.PopGetGem, { data: gains }); void gg.ui.openPanel(UIID.PopGetGem, { data: gains });
} }
/** 筛选切换 */ /** 页签切换 */
private _onTag(filterType: number, btn: Node): void { private _onTag(filterType: number, btn: Node): void {
if (this._filterType === filterType) return;
this._filterType = filterType; this._filterType = filterType;
this._selectTag(btn); this._selectTag(btn);
this._refreshBag(); this._rebuildBag();
} }
/** 标签底移到当前按钮 */ /** 标签底移到当前按钮 */
@ -127,7 +134,7 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
await gg.gem.ensureTable(); await gg.gem.ensureTable();
if (!this.isValid) return; if (!this.isValid) return;
this._buildElements(); this._buildElements();
this._refreshBag(); this._rebuildBag();
} }
/** 实例化五行 */ /** 实例化五行 */
@ -213,28 +220,107 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
if (icon?.isValid) icon.active = !!iconName; if (icon?.isValid) icon.active = !!iconName;
} }
/** 刷新仓库 */ /**
private _refreshBag(): void { * 重建当前页签仓库(打开 / 切页签 / 合成)。
* 镶嵌成败勿调用。
*/
private _rebuildBag(): void {
if (!this.ui_content?.isValid || !this._bagTpl?.isValid) { if (!this.ui_content?.isValid || !this._bagTpl?.isValid) {
console.warn(LOG, 'ui_content/bagItemTemplate 缺失'); console.warn(LOG, 'ui_content/bagItemTemplate 缺失');
return; return;
} }
this._stopBagFill();
for (let i = 0; i < this._bagItems.length; i++) { for (let i = 0; i < this._bagItems.length; i++) {
const n = this._bagItems[i]; const n = this._bagItems[i];
if (n?.isValid) n.destroy(); if (n?.isValid) n.destroy();
} }
this._bagItems.length = 0; this._bagItems.length = 0;
this._bagList = gg.gem.getBagList(this._filterType); this._bagList = gg.gem.getBagList(this._filterType);
for (let i = 0; i < this._bagList.length; i++) { this._bagFillIndex = 0;
const entry = this._bagList[i]; this._bagToken++;
const node = instantiate(this._bagTpl); this._pumpBagFill(this._bagToken);
node.parent = this.ui_content; }
node.active = true;
const grid = node.getComponent(GemGrid) || node.addComponent(GemGrid); /** 停掉仓库分帧 */
grid.setData(gg.gem.getGem(entry.gemId), entry.count); private _stopBagFill(): void {
(node as any)._gemId = entry.gemId; this._bagToken++;
this._bagItems.push(node); this._flow?.dispose();
this._flow = null;
}
/** 分帧填充仓库格 */
private _pumpBagFill(token: number): void {
if (!this.isValid || token !== this._bagToken) return;
if (!this.ui_content?.isValid || !this._bagTpl?.isValid) return;
const end = Math.min(this._bagFillIndex + BAG_PER_FRAME, this._bagList.length);
for (let i = this._bagFillIndex; i < end; i++) {
this._bagItems.push(this._spawnBagItem(this._bagList[i]));
}
this._bagFillIndex = end;
if (this._bagFillIndex >= this._bagList.length) return;
if (!this._flow) this._flow = new FrameFlowHost();
this._flow.waitFrames(1, () => this._pumpBagFill(token));
}
/** 创建一格仓库项 */
private _spawnBagItem(entry: IGemBagEntry): Node {
const node = instantiate(this._bagTpl!);
node.parent = this.ui_content;
node.active = true;
const grid = node.getComponent(GemGrid) || node.addComponent(GemGrid);
grid.setData(gg.gem.getGem(entry.gemId), entry.count);
(node as any)._gemId = entry.gemId;
return node;
}
/**
* 镶嵌/卸下后局部同步仓库数量。
* @param gemIds 涉及的宝石 id
*/
private _patchBagGems(gemIds: readonly number[]): void {
for (let i = 0; i < gemIds.length; i++) {
const gemId = Math.floor(Number(gemIds[i]) || 0);
if (gemId <= 0) continue;
const free = gg.gem.getFreeCount(gemId);
const conf = gg.gem.getGem(gemId);
const matchFilter = this._matchFilter(conf);
const idx = this._findBagItemIndex(gemId);
if (free <= 0 || !matchFilter) {
if (idx >= 0) this._removeBagItemAt(idx);
continue;
}
if (idx >= 0) {
const node = this._bagItems[idx];
node.getComponent(GemGrid)?.setData(conf, free);
this._bagList[idx] = { gemId, count: free };
continue;
}
const entry: IGemBagEntry = { gemId, count: free };
this._bagList.push(entry);
this._bagItems.push(this._spawnBagItem(entry));
}
}
/** 当前页签是否应展示该宝石 */
private _matchFilter(conf: ITableWeapon | null): boolean {
if (!conf) return false;
return Math.floor(Number(conf.type) || 0) === this._filterType;
}
/** 查找仓库格下标 */
private _findBagItemIndex(gemId: number): number {
for (let i = 0; i < this._bagItems.length; i++) {
if (Number((this._bagItems[i] as any)._gemId) === gemId) return i;
} }
return -1;
}
/** 移除一格仓库 */
private _removeBagItemAt(idx: number): void {
const node = this._bagItems[idx];
if (node?.isValid) node.destroy();
this._bagItems.splice(idx, 1);
this._bagList.splice(idx, 1);
} }
/** icons/weapon */ /** icons/weapon */
@ -250,7 +336,7 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items); node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items);
} }
// ---------- 拖拽 ---------- // ---------- 拖拽镶嵌 ----------
private _bindDrag(on: boolean): void { private _bindDrag(on: boolean): void {
if (this._dragListening === on) return; if (this._dragListening === on) return;
@ -332,12 +418,15 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
this._finishDrag(loc); this._finishDrag(loc);
return; return;
} }
// 镶嵌槽轻点看信息;仓库格由 GemGrid 自行处理
const gemId = this._dragGemId;
const fromSlot = this._dragFromElement > 0 && this._dragFromSlot >= 0;
this._endDrag(); this._endDrag();
if (fromSlot && gemId > 0) GemGrid.openInfo(gemId);
} }
private _beginDrag(loc: Vec2): void { private _beginDrag(loc: Vec2): void {
this._dragging = true; this._dragging = true;
this._setScrollEnabled(false);
this._spawnGhost(loc); this._spawnGhost(loc);
this._moveGhost(loc); this._moveGhost(loc);
} }
@ -359,21 +448,29 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
if (fromE === te && fromS === ts) return; if (fromE === te && fromS === ts) return;
if (!gg.gem.swapSlots(fromE, fromS, te, ts)) { if (!gg.gem.swapSlots(fromE, fromS, te, ts)) {
gg.ui.showToast('无法镶嵌到该位置'); gg.ui.showToast('无法镶嵌到该位置');
return;
} }
} else { this._refreshAllSlots();
if (!gg.gem.wearFromBag(te, ts, gemId)) { return;
gg.ui.showToast('无法镶嵌到该位置'); }
} const before = gg.gem.getSlotGem(te, ts);
if (!gg.gem.wearFromBag(te, ts, gemId)) {
gg.ui.showToast('无法镶嵌到该位置');
return;
} }
this._refreshAllSlots(); this._refreshAllSlots();
this._refreshBag(); const patch = [gemId];
if (before > 0 && before !== gemId) patch.push(before);
this._patchBagGems(patch);
return; return;
} }
if (fromE > 0 && fromS >= 0 && toBag) { if (fromE > 0 && fromS >= 0 && toBag) {
gg.gem.unequip(fromE, fromS); const worn = gg.gem.getSlotGem(fromE, fromS);
if (worn <= 0) return;
if (!gg.gem.unequip(fromE, fromS)) return;
this._refreshAllSlots(); this._refreshAllSlots();
this._refreshBag(); this._patchBagGems([worn]);
} }
} }
@ -387,6 +484,7 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
ghost.parent = this.node; ghost.parent = this.node;
ghost.active = true; ghost.active = true;
const grid = ghost.getComponent(GemGrid) || ghost.addComponent(GemGrid); const grid = ghost.getComponent(GemGrid) || ghost.addComponent(GemGrid);
grid.canClick = false;
grid.setData(conf, -1); grid.setData(conf, -1);
const op = ghost.getComponent(UIOpacity) || ghost.addComponent(UIOpacity); const op = ghost.getComponent(UIOpacity) || ghost.addComponent(UIOpacity);
op.opacity = GHOST_OPACITY; op.opacity = GHOST_OPACITY;
@ -409,16 +507,9 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
private _endDrag(): void { private _endDrag(): void {
this._clearGhost(); this._clearGhost();
this._setScrollEnabled(true);
this._resetDrag(); this._resetDrag();
} }
/** 拖拽时禁用仓库滚动 */
private _setScrollEnabled(on: boolean): void {
const sv = this._scrollNode?.getComponent(ScrollView);
if (sv) sv.enabled = on;
}
private _resetDrag(): void { private _resetDrag(): void {
this._pressing = false; this._pressing = false;
this._dragging = false; this._dragging = false;
@ -430,9 +521,8 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
private _hitBag(loc: Vec2): Node | null { private _hitBag(loc: Vec2): Node | null {
if (!this.ui_content?.isValid) return null; if (!this.ui_content?.isValid) return null;
this._hitPos.set(loc.x, loc.y); this._hitPos.set(loc.x, loc.y);
const kids = this._bagItems; for (let i = this._bagItems.length - 1; i >= 0; i--) {
for (let i = kids.length - 1; i >= 0; i--) { const child = this._bagItems[i];
const child = kids[i];
if (!child?.activeInHierarchy) continue; if (!child?.activeInHierarchy) continue;
const ut = child.getComponent(UITransform); const ut = child.getComponent(UITransform);
if (ut?.getBoundingBoxToWorld().contains(this._hitPos)) return child; if (ut?.getBoundingBoxToWorld().contains(this._hitPos)) return child;
@ -455,9 +545,9 @@ export class UIPanelGemstone extends Auto_PanelGemstone {
return null; return null;
} }
/** 是否落在仓库 ScrollView 区域 */ /** 是否落在仓库区(卸下) */
private _hitBagArea(loc: Vec2): boolean { private _hitBagArea(loc: Vec2): boolean {
const root = this._scrollNode; const root = this.ui_content;
if (!root?.isValid) return false; if (!root?.isValid) return false;
this._hitPos.set(loc.x, loc.y); this._hitPos.set(loc.x, loc.y);
const ut = root.getComponent(UITransform); const ut = root.getComponent(UITransform);

@ -0,0 +1,9 @@
{
"ver": "1.2.0",
"importer": "directory",
"imported": true,
"uuid": "63f51f60-1b58-4be0-bd82-dc19fbfe2f41",
"files": [],
"subMetas": {},
"userData": {}
}

@ -0,0 +1,49 @@
//*********************
// create by 流云
// 插件自动生成的节点脚本
// 请勿修改(自动生成会覆盖)
// 请通过子类继承使用
// 如何生成可直接调用的UI节点和组件?
// 1.在场景节点树中选中要生成的UI根节点
// 2.点击菜单"tool"->“生成UI脚本” 或者使用快捷键(快捷键:Ctrl+Shift+C)
// 3.点击后会在script/ui/目录中生成一个以选中节点命名的文件夹,文件夹中会生成两个脚本文件,“Auto_”开头的就是本文件,不可修改,另一个是“UI”+选中节点名称命名的脚本文件,可修改,是需要挂载到选中节点的脚本文件,给用户使用的写逻辑的脚本,只会生成一次
// 4.找到第3步描述的“UI”+选中节点名称命名的脚本文件,将其挂载到选中节点上(第一生成需要挂载,后续修改场景,重复生成的时候,该脚本不会再生成,不用担心会覆盖逻辑脚本)
// 下面是类型示例
// 1.按钮,节点名以“btn_”或者“Btn_”开头或者节点添加了Button组件, 例:btn_Close,会生成btn_Close节点和click_btn_Close()方法
// 2.标签,节点名以“lb_”开头, 例:lb_title,会生成lb_title的Label组件
// 3.输入框,节点名以“eb_”开头, 例:eb_input,会生成eb_input的EditBox组件
// 4.滚动视图,节点名以“sv_”开头, 例:sv_list,会生成sv_list的ScrollView组件
// 5.图片,节点名以“img_”或者sp_“”开头, 例:img_Icon,会生成img_Icon的Sprite组件
// 6.普通节点,节点名以“ui_”或者“Ui_”或者“UI_”开头, 例:ui_Content,会生成ui_Content的Node节点
//*********************
import { _decorator, Component, Node, find, Sprite, Label, Button, ScrollView, EditBox, RichText } from 'cc';
import { UIBase } from '../../../mx/module/ui/UIBase';
const { ccclass } = _decorator;
@ccclass('Auto_PopGemInfo')
export class Auto_PopGemInfo extends UIBase {
bgMask:Node;
lb_name:Label;
lb_lv:Label;
lb_desc:Label;
btnClose:Node;
onLoad(){
super.onLoad();
this.bgMask = find("bgMask",this.node);
this.lb_name = find("center/lb_name",this.node)?.getComponent(Label);
this.lb_lv = find("center/lb_lv",this.node)?.getComponent(Label);
this.lb_desc = find("center/lb_desc",this.node)?.getComponent(Label);
this.btnClose = find("center/btnClose",this.node);
}
onEnable() {
this.bgMask.on(Node.EventType.TOUCH_END, this.click_bgMask, this);
this.btnClose.on(Node.EventType.TOUCH_END, this.click_btnClose, this);
}
onDisable() {
this.bgMask.off(Node.EventType.TOUCH_END, this.click_bgMask, this);
this.btnClose.off(Node.EventType.TOUCH_END, this.click_btnClose, this);
}
click_bgMask() {}
click_btnClose() {}
}

@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "6650cdaf-d5a0-4bde-83d0-a568532780ee",
"files": [],
"subMetas": {},
"userData": {}
}

@ -0,0 +1,90 @@
//*********************
// create by 流云
// time: Thu Oct 08 2026
// desc: 宝石信息弹窗
//*********************
import { _decorator, find, Node, Sprite } from 'cc';
import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad';
import { GBundle } from '../../game/ConfigRes';
import { Auto_PopGemInfo } from './Auto_PopGemInfo';
const { ccclass } = _decorator;
const LOG = '[PopGemInfo]';
/**
* 宝石信息:iconbg=武器信息图标底框{quality};icon=Weapon.icon;
* lb_name / lb_desc 多语言;lb_lv=Lv.{quality}。
*/
@ccclass('UIPopGemInfo')
export class UIPopGemInfo extends Auto_PopGemInfo {
/** 品质底框 */
private _iconbg: Node | null = null;
/** 宝石图标 */
private _icon: Node | null = null;
onLoad(): void {
super.onLoad();
this._iconbg = find('center/iconbg', this.node);
this._icon = find('center/icon', this.node);
if (!this._iconbg) console.warn(LOG, 'iconbg 缺失');
if (!this._icon) console.warn(LOG, 'icon 缺失');
}
onInit(): void {
this._fill(this._resolveGemId());
}
click_bgMask(): void {
void this.close(2);
}
click_btnClose(): void {
void this.close(2);
}
/** 解析打开参数:数字 gemId 或 { gemId } */
private _resolveGemId(): number {
const raw = this.prema;
if (raw !== null && typeof raw === 'object') {
return Math.floor(Number((raw as { gemId?: number }).gemId) || 0);
}
return Math.floor(Number(raw) || 0);
}
/** 按宝石 id 刷新 */
private _fill(gemId: number): void {
const conf = gemId > 0 ? gg.gem.getGem(gemId) : null;
if (!conf) {
console.warn(LOG, '宝石配表缺失', gemId);
return;
}
const quality = Math.floor(Number(conf.quality) || 0);
if (quality < 1 || quality > 5) console.warn(LOG, '品质无对应底框', gemId, quality);
this._setWeaponSprite(this._iconbg, `weapon/武器信息图标底框${quality}`);
const iconName = String(conf.icon || '').trim();
if (!iconName) console.warn(LOG, 'icon 缺失', gemId);
this._setWeaponSprite(this._icon, iconName ? `weapon/${iconName}` : '');
if (this.lb_name) this.lb_name.string = gg.lang.get(conf.name);
if (this.lb_lv) this.lb_lv.string = `Lv.${quality}`;
if (this.lb_desc) this.lb_desc.string = gg.lang.get(conf.desc);
}
/** icons/weapon 下图 */
private _setWeaponSprite(node: Node | null, path: string): void {
if (!node?.isValid) return;
if (!path) {
node.active = false;
node.getComponent(SpriteLoad)?.clear();
const sp = node.getComponent(Sprite);
if (sp) sp.spriteFrame = null;
return;
}
node.active = true;
if (!node.getComponent(Sprite)) node.addComponent(Sprite);
node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items);
}
}

@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "7f13fa9c-e418-4960-a180-d16633a2eca9",
"files": [],
"subMetas": {},
"userData": {}
}

@ -1,6 +1,6 @@
# 需求:宝石装备与合成 # 需求:宝石装备与合成
> 状态:局外 UI / 存档 / 合成本期实现;局内属性与技能生效后续切片。 > 状态:局外 UI / 存档 / 合成已实现;局内属性与技能已接入战斗。
## 背景 ## 背景
@ -38,6 +38,30 @@
3. 多轮直到无可合成。 3. 多轮直到无可合成。
4. 弹窗展示:各宝石「合成后可用数 − 合成前可用数」中 `delta > 0` 的条目。 4. 弹窗展示:各宝石「合成后可用数 − 合成前可用数」中 `delta > 0` 的条目。
## 局内(后续) ## UI 注意
通过 `gg.gem.getElementSlots(elementType)` 读取镶嵌,再叠加 `attritubes` / `act_skill`。本期不接入战斗。 - 仓库按页签展示,无「全部」、无滚动:`btn0~btn5` → `Weapon.type` 1~6(通用/金木水火土),默认通用。
- 仓库格分帧实例化进 `ui_content`(Layout 排布);交互为直接拖拽镶嵌/卸下。
- 仓库全量重建仅:打开页 / 切页签 / 一键合成。镶嵌成功只局部改数量与槽位;失败不刷列表。
- 仓库项 `bg` 用 `宝石框大{quality}`;镶嵌位 `g1~g4` 仍用 `宝石框小`。
## 局内(已接入)
五行数字上阵时(`TowerLoadoutService.setupCombat`):
1. 本机玩家:`gg.gem.getElementSlots(Hero.herotype)` 读该五行最多 4 槽宝石。
2. 每颗 `attritubes` 写入该数字属性(多槽累加);刷新上阵前清除旧 `gem_` modifier。
3. 有 `act_skill` 的宝石接到原有武器技能槽(主动 / 被动 / atk_buff);同五行多颗技能宝石时取品质更高者(配表异常时 warn)。
4. AI 对手不读本机镶嵌。
### `attritubes` 类型
| type | 含义 | 战斗写入 |
|------|------|----------|
| 1 | 攻击万分比 | Attack Mul |
| 2 | 暴击万分比 | CritRate Add |
| 3 | 暴击伤害万分比 | CritDamage Add |
| 4 | 攻速万分比 | aspd_add |
| 5 | 伤害加成万分比 | dmg_mul |
实现:`assets/script/battle/combat/GemBattleEffect.ts`。

Loading…
Cancel
Save