You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
AdLike2048/assets/script/ui/PanelShenjiang/shenjiangItem.ts

228 lines
9.5 KiB

import { _decorator, Button, Color, Component, Label, Node, sp, Sprite } from 'cc';
import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad';
import { ensureSpineSkeletonDataRuntime } from 'db://assets/mx/module/lang/spineRuntimePatch';
import { SpineBatch } from 'db://assets/resources/multSpines/MultSpine';
import { GBundle } from '../../game/ConfigRes';
import type { IMingjiangkuBagEntry } from '../../manager/EquipDataManager';
import { RedDotView } from 'db://assets/mx/module/redDot/RedDotView';
const { ccclass, property } = _decorator;
const LOG = '[shenjiangItem]';
const SPINE_DIM = new Color(0x99, 0x99, 0x99, 255);
const SPINE_NORMAL = new Color(255, 255, 255, 255);
const WEAPON_SPINE_ANIM = '待机';
/** MultSpine 置灰:类型 1 + 插值 0.999(见 MultSpine.ts / Mult-spine.effect) */
const SPINE_GRAY_SHADER = 1.999;
/** 武器名不少于该字数时缩小 name 图(含 5 字) */
const WEAPON_NAME_LONG_LEN = 5;
/** openNode.name:未超长=1,超长=0.8 */
const OPEN_NAME_SCALE_NORMAL = 1;
const OPEN_NAME_SCALE_LONG = 0.8;
/** suipianNode.name:未超长=0.7,超长=0.56 */
const SUIPIAN_NAME_SCALE_NORMAL = 0.7;
const SUIPIAN_NAME_SCALE_LONG = 0.56;
type SkelShader = sp.Skeleton & { shaderVar?: number };
/**
* 神将武器库格子
* quality:icons/weapon/武器底0~4
* openNode/suipianNode:name=icons/weapon/{name};type1~4 按 Weapon.type 显隐,Spine 皮肤为 Weapon.skin
*/
@ccclass('shenjiangItem')
export class shenjiangItem extends Component {
/** 碎片节点(含 type1~4、lb_suipian) */
@property(Node)
suipianNode: Node = null;
/** 当前格子数据 */
private _data: IMingjiangkuBagEntry | null = null;
/** 当前格子数据 */
get data(): IMingjiangkuBagEntry | null {
return this._data;
}
/** 成品拖拽图标节点(openNode 下当前 type 的 Spine) */
getDragIconNode(): Node | null {
const openNode = this.node.getChildByName('openNode');
if (!openNode?.activeInHierarchy) return null;
const weapon = this._data?.equipId ? gg.equip.getEquip(this._data.equipId) : null;
const type = Number(weapon?.type) | 0;
if (type >= 1 && type <= 4) {
return openNode.getChildByName(`type${type}`) ?? null;
}
return null;
}
/** 绑定格子数据并刷新展示 */
setData(data: IMingjiangkuBagEntry): void {
this._data = data;
if (!this.suipianNode) this.suipianNode = this.node.getChildByName('suipianNode');
const openNode = this.node.getChildByName('openNode');
const qualityNode = this.node.getChildByName('quality');
const isFrag = data.kind === 'fragment';
const weapon = data.equipId > 0 ? gg.equip.getEquip(data.equipId) : null;
if (!weapon) console.log(LOG, '武器表缺失', data.equipId);
const weaponType = Number(weapon?.type) | 0;
const skin = String(weapon?.skin ?? '').trim();
const weaponName = String(weapon?.name ?? '').trim();
if (weaponType < 1 || weaponType > 4) console.warn(LOG, 'type 无效', data.equipId, weaponType);
if (!skin) console.warn(LOG, 'skin 缺失', data.equipId);
if (!weaponName) console.warn(LOG, 'name 缺失', data.equipId);
if (openNode) openNode.active = !isFrag;
if (this.suipianNode) this.suipianNode.active = isFrag;
const bgIndex = this._equipQualityBgIndex(Number(weapon?.quality), data.equipId);
this._setSprite(qualityNode, `weapon/武器底${bgIndex}`);
const longName = weaponName.length >= WEAPON_NAME_LONG_LEN;
const openScale = longName ? OPEN_NAME_SCALE_LONG : OPEN_NAME_SCALE_NORMAL;
const suipianScale = longName ? SUIPIAN_NAME_SCALE_LONG : SUIPIAN_NAME_SCALE_NORMAL;
if (!isFrag && openNode) {
this._setWeaponNameNode(openNode, weaponName, openScale);
this._applyWeaponSpineGroup(openNode, weaponType, skin, false);
this._refreshNewWeaponTag(openNode, data.equipId, !isFrag);
const lbNum = openNode.getChildByName('numBg')?.getChildByName('lb_weaponNum')?.getComponent(Label);
if (lbNum) lbNum.string = `${Math.max(0, data.count)}`;
}
if (isFrag && this.suipianNode) {
this._setWeaponNameNode(this.suipianNode, weaponName, suipianScale);
this._applyWeaponSpineGroup(this.suipianNode, weaponType, skin, true);
const lbFrag = this.suipianNode.getChildByName('lb_suipian')?.getComponent(Label);
const need = Number(data.need) > 0 ? Number(data.need) : gg.equip.getComposeNeed(data.equipId);
if (need <= 0) console.log(LOG, '合成需求缺失', data.equipId);
if (lbFrag) lbFrag.string = `${data.count}/${Math.max(0, need)}`;
}
const btns = this.node.getComponentsInChildren(Button);
for (let i = 0; i < btns.length; i++) {
btns[i].interactable = false;
btns[i].enabled = false;
}
RedDotView.setLocal(this.node, false);
}
/** 隐藏 openNode 新武器角标 */
hideNewWeaponTag(): void {
const openNode = this.node.getChildByName('openNode');
this._refreshNewWeaponTag(openNode, 0, false);
}
/** openNode/newWeapon:新成品武器角标 */
private _refreshNewWeaponTag(openNode: Node | null, equipId: number, show: boolean): void {
const tag = openNode?.getChildByName('newWeapon');
if (!tag?.isValid) return;
tag.active = show && gg.equip.isNewWeaponBadgeVisible(equipId);
}
/** 成品可用数量(库存-已穿戴) */
setCount(count: number): void {
if (this._data) this._data.count = Math.max(0, count | 0);
if (this._data?.kind === 'fragment') return;
const lbNum = this.node.getChildByName('openNode')?.getChildByName('numBg')?.getChildByName('lb_weaponNum')?.getComponent(Label);
if (lbNum) lbNum.string = `${this._data?.count ?? 0}`;
}
/** 设置武器名图并缩放 name 节点(贴图异步加载完成后会再设一次缩放) */
private _setWeaponNameNode(parent: Node | null, weaponName: string, scale: number): void {
const nameNode = parent?.getChildByName('name');
if (!nameNode?.isValid) return;
const applyScale = (): void => {
nameNode.setScale(scale, scale, 1);
};
const path = weaponName ? `weapon/${weaponName}` : '';
if (!path) {
nameNode.active = false;
applyScale();
return;
}
nameNode.active = true;
if (!nameNode.getComponent(Sprite)) nameNode.addComponent(Sprite);
nameNode.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items, () => {
applyScale();
});
applyScale();
}
/** 按武器类型显隐 type1~4,并设置 Spine 皮肤 */
private _applyWeaponSpineGroup(parent: Node, weaponType: number, skin: string, dimmed: boolean): void {
let matched = false;
for (let t = 1; t <= 4; t++) {
const typeNode = parent.getChildByName(`type${t}`);
if (!typeNode) continue;
const show = t === weaponType;
typeNode.active = show;
if (!show) continue;
matched = true;
this._applyWeaponSpine(typeNode, skin, dimmed);
}
if (!matched && weaponType >= 1 && weaponType <= 4) {
console.log(LOG, 'type 节点缺失', parent.name, weaponType);
}
}
/** 设置单个武器 Spine 皮肤与待机 */
private _applyWeaponSpine(typeNode: Node, skin: string, dimmed: boolean): void {
const skel = typeNode.getComponent(sp.Skeleton) ?? typeNode.getComponentInChildren(sp.Skeleton);
if (!skel) {
console.log(LOG, 'Spine 缺失', typeNode.name);
return;
}
if (skel.skeletonData) ensureSpineSkeletonDataRuntime(skel.skeletonData);
if (skin) {
try {
skel.setSkin(skin);
skel.setSlotsToSetupPose();
} catch (e) {
console.log(LOG, 'setSkin 失败', skin, String(e));
}
}
try {
skel.setAnimation(0, WEAPON_SPINE_ANIM, true);
} catch (e) {
console.log(LOG, 'setAnimation 失败', WEAPON_SPINE_ANIM, String(e));
}
this._setSpineDimmed(skel, dimmed);
}
/**
* Spine 置灰:MultSpine 合批走 shaderVar(1.999),否则走 skel.color(同 PanelShenjiang 神将提示)
*/
private _setSpineDimmed(skel: sp.Skeleton, dimmed: boolean): void {
const s = skel as SkelShader;
if (SpineBatch.active() && typeof s.shaderVar === 'number') {
s.shaderVar = dimmed ? SPINE_GRAY_SHADER : 0;
skel.color = SPINE_NORMAL;
return;
}
s.shaderVar = 0;
skel.color = dimmed ? SPINE_DIM : SPINE_NORMAL;
}
/** 成品品质 → 武器底 0~4(白/绿/蓝/紫/橙) */
private _equipQualityBgIndex(quality: number, equipId: number): number {
const q = Number(quality) | 0;
if (q < 0 || q > 4) {
console.log(LOG, '品质无效', equipId, q);
}
return q;
}
private _setSprite(node: Node | null, path: string): void {
if (!node?.isValid) return;
if (!path) {
node.active = false;
return;
}
node.active = true;
if (!node.getComponent(Sprite)) node.addComponent(Sprite);
node.getOrAddComponent(SpriteLoad).setSprite(path, GBundle.Items);
}
}