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.
 
 
 
 

130 lines
4.1 KiB

import { _decorator, sp } from 'cc';
import type { EntityId } from 'db://assets/mx/module/ecs';
import { isComboHero, 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<ITableHero>(TableNames.Hero, this._heroId)
: null);
this._heroId = Number(data?.id) || this._heroId;
const useWeapon = isElementHero(data) || (!!data && isComboHero(data));
this.visualChannel.bind(this._heroSkin, this.node, this._heroId, 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.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);
}
}