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.
135 lines
4.3 KiB
135 lines
4.3 KiB
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<ITableHero>(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);
|
|
}
|
|
}
|
|
|