import { _decorator, sp } from 'cc'; import type { EntityId } from 'db://assets/mx/module/ecs'; import { isElementHero } from '../config/BattleHeroTable'; import { Skill } from '../skill/Skill'; import { HeroSkin } from '../view/HeroSkin'; import { TableNames } from '../../game/ConfigTableData'; const { ccclass } = _decorator; /** * 通用英雄表现壳:字形 + 可选武器通道,三槽均走 SkillPresenterRouter。 * 组合英雄激活后加载当前装备武器,锚在组合首字靠左。 */ @ccclass('HeroActor') export class HeroActor extends Skill { protected _heroSkin: HeroSkin | null = null; private _heroId = 0; private _entityId: EntityId | null = null; /** 组合武器未显示时的对账次数 */ private _weaponRetry = 0; bindEntity(entityId: EntityId): void { this._entityId = entityId; this.visualChannel.setCaster(entityId); this._bindChannel(); if (this.isActive && !this.isPresentationBusy()) this.onIdle(); } detachPooledFx(): void { this.visualChannel.release(); } protected onLoad(): void { this._bindChannel(); this.onIdle(); } protected onDisable(): void { this._stopWeaponRetry(); this.teardownPresentation(); } protected onDestroy(): void { this.teardownLifecycle(); this.visualChannel.release(); } abortPresentation(): void { this.abortPresentationCast(); this.visualChannel.interruptCast(); if (this.isActive) this.onIdle(); else this.visualChannel.hideWeapon(); } onIdle(): void { this.visualChannel.setCaster(this._entityId); this.visualChannel.playIdle(); this._retryIdleWeapon(); } onUpdateActive(active: boolean): void { this._bindChannel(); this.visualChannel.setCaster(this._entityId); if (active) { this.onIdle(); return; } this._stopWeaponRetry(); this.visualChannel.hideWeapon(); this._heroSkin?.playIdleAnim(); } /** 绑定字形;五行 / 组合英雄启用武器通道;五行仅攻击显示武器 */ protected _bindChannel(): void { this._heroSkin = this.node.getComponent(HeroSkin); const data = this._heroSkin?.Data ?? (this._heroId > 0 ? gg.data.table.getTableData(TableNames.Hero, this._heroId) : null); this._heroId = Number(data?.id) || this._heroId; const useWeapon = isElementHero(data); this.visualChannel.bind(this._heroSkin, this.node, this._heroId, useWeapon); this.visualChannel.setAttackOnlyWeapon(useWeapon); } /** * 单字英雄攻效 Spine 切阵营皮(敌方 Hero.res+「2」)。 * @param atkAnim 攻效预制体上的 Skeleton */ protected syncAtkFxSkin(atkAnim: sp.Skeleton | null): void { this._heroSkin?.applyHeroResSkinTo(atkAnim); } /** HeroActorHost 创建后写入英雄 id(皮肤可能稍后 setData) */ setHeroId(heroId: number): void { this._heroId = Math.floor(Number(heroId) || 0); this._bindChannel(); if (this.isActive && !this.isPresentationBusy()) this.onIdle(); } /** 首字格 / 武器预制体晚于激活:下一帧再挂待机武器(五行数字不补挂) */ private _retryIdleWeapon(): void { if (this.visualChannel.attackOnlyWeapon) { this._weaponRetry = 0; return; } if (!this.isActive || !this.visualChannel.useWeapon || this.isPresentationBusy()) { this._weaponRetry = 0; return; } if (this.visualChannel.hasVisibleIdleWeapon()) { this._weaponRetry = 0; return; } if (this._weaponRetry >= 60) { console.warn('[HeroActor] 组合武器未显示', this._heroId, this._entityId); return; } this._weaponRetry++; this.scheduleOnce(this._onWeaponRetry, 0); } /** 武器对账回调 */ private readonly _onWeaponRetry = (): void => { if (!this.isValid || !this.isActive) return; this.onIdle(); }; /** 作废未触发的武器对账 */ private _stopWeaponRetry(): void { this._weaponRetry = 0; this.unschedule(this._onWeaponRetry); } }