|
|
|
|
import { Color, Label, Node, Sprite, Tween, tween } from 'cc';
|
|
|
|
|
import { SpriteLoad } from 'db://assets/mx/components/SpriteLoad';
|
|
|
|
|
import { GBundle, GPath } from '../../game/ConfigRes';
|
|
|
|
|
import { DailyShopKind, DailyShopParsed } from '../../manager/DailyShopTypes';
|
|
|
|
|
import { DailyShopLotteryIcon, DailyShopOwnedDesc } from '../PopDailyShopLottery/DailyShopLotteryIcon';
|
|
|
|
|
import { BattleBuffCarryStore } from '../../manager/battleBuff/BattleBuffCarryStore';
|
|
|
|
|
import {
|
|
|
|
|
isBattleBuffDragUnitLevelUp,
|
|
|
|
|
resolveBattleGameSkillDragKind,
|
|
|
|
|
resolveBattleGameSkillClickKind,
|
|
|
|
|
BattleGameSkillDragKind,
|
|
|
|
|
isBattleBuffDragRecycleToPeach,
|
|
|
|
|
} from '../../manager/battleBuff/BattleBuffActiveSkillUtil';
|
|
|
|
|
import type { ProjectBattleHandle } from '../../battle/runtime/BattleRuntime';
|
|
|
|
|
import { getBattleGameRandomActiveSkillHost } from './BattleGameRandomActiveSkillHost';
|
|
|
|
|
|
|
|
|
|
/** 径向 CD 遮罩起点:12 点钟(Cocos fillStart 0=3 点,逆时针计量) */
|
|
|
|
|
export const SKILL_CD_MASK_FILL_START = 0.25;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化技能 CD 径向遮罩(12 点起顺时针填充,fillRange 取正)。
|
|
|
|
|
* @param sprite mask 节点 Sprite
|
|
|
|
|
*/
|
|
|
|
|
export function setupSkillCdMaskSprite(sprite: Sprite): void {
|
|
|
|
|
sprite.type = Sprite.Type.FILLED;
|
|
|
|
|
sprite.fillType = Sprite.FillType.RADIAL;
|
|
|
|
|
sprite.fillCenter.set(0.5, 0.5);
|
|
|
|
|
sprite.fillStart = SKILL_CD_MASK_FILL_START;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 刷新 CD 遮罩比例(1=满 CD,0=无遮罩)。
|
|
|
|
|
* @param sprite mask 节点 Sprite
|
|
|
|
|
* @param cdRatio 剩余 CD 占比
|
|
|
|
|
*/
|
|
|
|
|
export function applySkillCdMaskFillRange(sprite: Sprite, cdRatio: number): void {
|
|
|
|
|
sprite.fillRange = Math.max(0, Math.min(1, cdRatio));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 战斗界面 buff 名称描边色(按品质) */
|
|
|
|
|
const SKILL_NAME_OUTLINE_HEX: Record<number, string> = {
|
|
|
|
|
1: '#386c42',
|
|
|
|
|
2: '#285c6d',
|
|
|
|
|
3: '#623073',
|
|
|
|
|
4: '#734930',
|
|
|
|
|
};
|
|
|
|
|
/** 就绪态 light 旋转一圈耗时(秒) */
|
|
|
|
|
const SKILL_LIGHT_ROTATE_SEC = 2;
|
|
|
|
|
|
|
|
|
|
/** skill 槽 bind 选项 */
|
|
|
|
|
export type BattleGameSkillSlotBindOpts = {
|
|
|
|
|
/** 剩余 CD 秒;省略则满 CD */
|
|
|
|
|
cdLeftSec?: number;
|
|
|
|
|
/** CD 总长;省略则用 parsed.cd */
|
|
|
|
|
cdTotalSec?: number;
|
|
|
|
|
/** 槽位携带 buff(七十二变 roll 时与 display 不同) */
|
|
|
|
|
carriedParsed?: DailyShopParsed | null;
|
|
|
|
|
/** 当前应施放 buff(与 AI castParsed 一致;roll 后与 display 相同) */
|
|
|
|
|
castParsed?: DailyShopParsed | null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 单个 skill 格:品质 / 图标 / 名称 / CD 遮罩 */
|
|
|
|
|
export class BattleGameDailyShopSkillSlot {
|
|
|
|
|
private readonly _root: Node;
|
|
|
|
|
private readonly _light: Node | null;
|
|
|
|
|
private readonly _quality: Node | null;
|
|
|
|
|
private readonly _icon: Node | null;
|
|
|
|
|
private readonly _name: Label | null;
|
|
|
|
|
private readonly _mask: Node | null;
|
|
|
|
|
private readonly _maskSprite: Sprite | null;
|
|
|
|
|
|
|
|
|
|
private _cdTotalSec = 0;
|
|
|
|
|
private _cdLeftSec = 0;
|
|
|
|
|
/** 展示用 buff(图标/名称) */
|
|
|
|
|
private _parsed: DailyShopParsed | null = null;
|
|
|
|
|
/** 槽位携带 buff(七十二变本体) */
|
|
|
|
|
private _carriedParsed: DailyShopParsed | null = null;
|
|
|
|
|
/** 当前应施放 buff(七十二变 roll 后为随机 buff;与 AI castParsed 同源) */
|
|
|
|
|
private _castParsed: DailyShopParsed | null = null;
|
|
|
|
|
/** light 旋转 tween 是否已启动 */
|
|
|
|
|
private _lightRotating = false;
|
|
|
|
|
|
|
|
|
|
/** @param root skill1 / skill2 根节点 */
|
|
|
|
|
constructor(root: Node, private readonly _slotIndex: number) {
|
|
|
|
|
this._root = root;
|
|
|
|
|
this._light = root.getChildByName('light');
|
|
|
|
|
const qualityNode = root.getChildByName('quality');
|
|
|
|
|
this._quality = qualityNode;
|
|
|
|
|
this._icon = qualityNode?.getChildByName('icon') ?? root.getChildByName('icon');
|
|
|
|
|
this._name = root.getChildByName('name')?.getComponent(Label) ?? null;
|
|
|
|
|
this._mask = root.getChildByName('mask');
|
|
|
|
|
this._maskSprite = this._mask?.getComponent(Sprite) ?? null;
|
|
|
|
|
if (this._maskSprite?.isValid) setupSkillCdMaskSprite(this._maskSprite);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 绑定槽位 buff;无 buff 时隐藏根节点 */
|
|
|
|
|
bind(parsed: DailyShopParsed | null, opts?: BattleGameSkillSlotBindOpts): void {
|
|
|
|
|
if (!parsed || parsed.kind !== DailyShopKind.Active) {
|
|
|
|
|
this._root.active = false;
|
|
|
|
|
this._cdTotalSec = 0;
|
|
|
|
|
this._cdLeftSec = 0;
|
|
|
|
|
this._parsed = null;
|
|
|
|
|
this._carriedParsed = null;
|
|
|
|
|
this._castParsed = null;
|
|
|
|
|
this._stopLightRotate();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this._parsed = parsed;
|
|
|
|
|
this._carriedParsed = opts?.carriedParsed ?? parsed;
|
|
|
|
|
this._castParsed = opts?.castParsed ?? parsed;
|
|
|
|
|
this._root.active = true;
|
|
|
|
|
const cdBaseParsed = this._carriedParsed ?? parsed;
|
|
|
|
|
this._cdTotalSec = Math.max(0, opts?.cdTotalSec ?? (cdBaseParsed.cd | 0));
|
|
|
|
|
this._cdLeftSec = opts?.cdLeftSec != null
|
|
|
|
|
? Math.max(0, opts.cdLeftSec)
|
|
|
|
|
: this._cdTotalSec;
|
|
|
|
|
|
|
|
|
|
const quality = Math.max(1, Math.floor(Number(parsed.quality) || 0));
|
|
|
|
|
this._setQualitySprite(quality);
|
|
|
|
|
this._setIconSprite(DailyShopLotteryIcon.itemIconName(parsed));
|
|
|
|
|
this._setName(parsed.name, quality);
|
|
|
|
|
this._refreshCdVisual();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 帧更新 CD 遮罩 */
|
|
|
|
|
tick(dt: number): void {
|
|
|
|
|
if (this._cdLeftSec <= 0) return;
|
|
|
|
|
this._cdLeftSec = Math.max(0, this._cdLeftSec - dt);
|
|
|
|
|
this._refreshCdVisual();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 槽位索引(0=skill1,1=skill2) */
|
|
|
|
|
get slotIndex(): number {
|
|
|
|
|
return this._slotIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 根节点 */
|
|
|
|
|
get root(): Node {
|
|
|
|
|
return this._root;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 图标节点(拖拽影子) */
|
|
|
|
|
getIconNode(): Node | null {
|
|
|
|
|
return this._icon?.isValid ? this._icon : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 是否可施放(未在 CD) */
|
|
|
|
|
isReady(): boolean {
|
|
|
|
|
return this._root.active && this._cdLeftSec <= 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 槽位携带 buff 解析(七十二变本体) */
|
|
|
|
|
getCarriedParsed(): DailyShopParsed | null {
|
|
|
|
|
return this._carriedParsed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 当前应施放/交互的 buff(七十二变 roll 后为随机 buff) */
|
|
|
|
|
getCastParsed(): DailyShopParsed | null {
|
|
|
|
|
return this._castParsed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 当前应施放 buff 表行 */
|
|
|
|
|
getBuffRow(): ITableBattlebuff | null {
|
|
|
|
|
return (this.getCastParsed()?.row as ITableBattlebuff) ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 当前应施放 buff 解析结果 */
|
|
|
|
|
getParsed(): DailyShopParsed | null {
|
|
|
|
|
return this.getCastParsed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 展示 buff id(roll 后与携带 id 可能不同) */
|
|
|
|
|
getBoundBuffId(): number {
|
|
|
|
|
return this._parsed?.id ?? 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 槽位携带 buff id;未绑定为 0 */
|
|
|
|
|
getCarriedBuffId(): number {
|
|
|
|
|
return this._carriedParsed?.id ?? 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 槽位是否已携带 buff(七十二变 CD 中也为 true) */
|
|
|
|
|
hasCarriedBuff(): boolean {
|
|
|
|
|
return this.getCarriedBuffId() > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 当前应走的 skill 拖拽类型(七十二变 roll 后按变化 buff) */
|
|
|
|
|
getCastDragKind(): BattleGameSkillDragKind | null {
|
|
|
|
|
const row = (this._castParsed?.row as ITableBattlebuff) ?? null;
|
|
|
|
|
return resolveBattleGameSkillDragKind(row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 仅同步 CD 遮罩(不改图标/名称)。
|
|
|
|
|
* @param cdLeftSec 剩余秒
|
|
|
|
|
* @param cdTotalSec 总秒
|
|
|
|
|
*/
|
|
|
|
|
syncCooldown(cdLeftSec: number, cdTotalSec: number): void {
|
|
|
|
|
if (!this._root.active) return;
|
|
|
|
|
this._cdTotalSec = Math.max(0, cdTotalSec);
|
|
|
|
|
this._cdLeftSec = Math.max(0, cdLeftSec);
|
|
|
|
|
this._refreshCdVisual();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 是否为主动拖放升级 buff */
|
|
|
|
|
isDragUnitLevelUpBuff(): boolean {
|
|
|
|
|
return isBattleBuffDragUnitLevelUp(this.getBuffRow());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 是否为主动缚仙网 buff(Phase1 无) */
|
|
|
|
|
isDragBindNetBuff(): boolean {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 是否可拖拽施放(Phase1 无商店主动拖拽) */
|
|
|
|
|
isDraggableSkill(): boolean {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 是否乾坤袋回收 buff */
|
|
|
|
|
isRecycleToPeachSkill(): boolean {
|
|
|
|
|
return isBattleBuffDragRecycleToPeach(this.getBuffRow());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 是否有主动 skill 交互(拖拽 / 点击) */
|
|
|
|
|
hasBattleSkillInteraction(): boolean {
|
|
|
|
|
return this._root.active && (
|
|
|
|
|
this.isDraggableSkill()
|
|
|
|
|
|| this.isClickSkill()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 是否可点击施放(万神幡等) */
|
|
|
|
|
isClickSkill(): boolean {
|
|
|
|
|
return resolveBattleGameSkillClickKind(this.getBuffRow()) != null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 施放后开始冷却 */
|
|
|
|
|
startCooldown(): void {
|
|
|
|
|
if (this._cdTotalSec <= 0) return;
|
|
|
|
|
this._cdLeftSec = this._cdTotalSec;
|
|
|
|
|
this._refreshCdVisual();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 品质底图 */
|
|
|
|
|
private _setQualitySprite(quality: number): void {
|
|
|
|
|
if (!this._quality?.isValid) return;
|
|
|
|
|
const sl = this._quality.getComponent(SpriteLoad);
|
|
|
|
|
if (!sl) {
|
|
|
|
|
console.log('[BattleGame] quality 缺少 SpriteLoad', this._root.name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
sl.setSprite(GPath.BattleGameSkillBg(quality), GBundle.BattleGame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 技能图标 */
|
|
|
|
|
private _setIconSprite(iconName: string): void {
|
|
|
|
|
if (!this._icon?.isValid) return;
|
|
|
|
|
const sl = this._icon.getComponent(SpriteLoad);
|
|
|
|
|
if (!sl) {
|
|
|
|
|
console.log('[BattleGame] icon 缺少 SpriteLoad', this._root.name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
sl.setSprite(GPath.ShopSkill(iconName), GBundle.Items);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 名称与描边 */
|
|
|
|
|
private _setName(name: string, quality: number): void {
|
|
|
|
|
if (!this._name?.isValid) return;
|
|
|
|
|
this._name.string = name;
|
|
|
|
|
const hex = SKILL_NAME_OUTLINE_HEX[quality];
|
|
|
|
|
if (!hex) {
|
|
|
|
|
console.log('[BattleGame] 未知 buff 品质描边', quality, this._root.name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this._name.enableOutline = true;
|
|
|
|
|
this._name.outlineColor = Color.fromHEX(new Color(), hex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** light / mask 与 fillRange */
|
|
|
|
|
private _refreshCdVisual(): void {
|
|
|
|
|
const onCd = this._cdLeftSec > 0;
|
|
|
|
|
const showLight = !onCd;
|
|
|
|
|
if (this._light?.isValid) {
|
|
|
|
|
this._light.active = showLight;
|
|
|
|
|
}
|
|
|
|
|
this._syncLightRotate(showLight && this._root.active);
|
|
|
|
|
if (this._mask?.isValid) {
|
|
|
|
|
this._mask.active = onCd;
|
|
|
|
|
}
|
|
|
|
|
if (onCd && this._maskSprite?.isValid && this._cdTotalSec > 0) {
|
|
|
|
|
applySkillCdMaskFillRange(this._maskSprite, this._cdLeftSec / this._cdTotalSec);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 就绪态 light 持续旋转;CD 中停止并归零 */
|
|
|
|
|
private _syncLightRotate(active: boolean): void {
|
|
|
|
|
const light = this._light;
|
|
|
|
|
if (!light?.isValid) return;
|
|
|
|
|
if (active) {
|
|
|
|
|
if (this._lightRotating) return;
|
|
|
|
|
this._lightRotating = true;
|
|
|
|
|
Tween.stopAllByTarget(light);
|
|
|
|
|
light.angle = 0;
|
|
|
|
|
tween(light)
|
|
|
|
|
.by(SKILL_LIGHT_ROTATE_SEC, { angle: -360 })
|
|
|
|
|
.repeatForever()
|
|
|
|
|
.start();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
this._stopLightRotate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 停止 light 旋转 tween */
|
|
|
|
|
private _stopLightRotate(): void {
|
|
|
|
|
this._lightRotating = false;
|
|
|
|
|
const light = this._light;
|
|
|
|
|
if (!light?.isValid) return;
|
|
|
|
|
Tween.stopAllByTarget(light);
|
|
|
|
|
light.angle = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 战斗界面 ui_selfskill1List:skill1 / skill2 展示每日商店主动 buff。
|
|
|
|
|
*/
|
|
|
|
|
export class BattleGameDailyShopSkillView {
|
|
|
|
|
private readonly _skill1: BattleGameDailyShopSkillSlot | null;
|
|
|
|
|
private readonly _skill2: BattleGameDailyShopSkillSlot | null;
|
|
|
|
|
private _battle: ProjectBattleHandle | null = null;
|
|
|
|
|
private _onDisplayChanged: (() => void) | null = null;
|
|
|
|
|
private _ownedDesc: DailyShopOwnedDesc | null = null;
|
|
|
|
|
|
|
|
|
|
/** @param skillList ui_selfskill1List 节点 */
|
|
|
|
|
constructor(skillList: Node | null | undefined) {
|
|
|
|
|
const skill1Node = skillList?.getChildByName('skill1') ?? null;
|
|
|
|
|
const skill2Node = skillList?.getChildByName('skill2') ?? null;
|
|
|
|
|
this._skill1 = skill1Node?.isValid ? new BattleGameDailyShopSkillSlot(skill1Node, 0) : null;
|
|
|
|
|
this._skill2 = skill2Node?.isValid ? new BattleGameDailyShopSkillSlot(skill2Node, 1) : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 取槽位展示器 */
|
|
|
|
|
getSlot(slotIndex: number): BattleGameDailyShopSkillSlot | null {
|
|
|
|
|
if (slotIndex === 0) return this._skill1;
|
|
|
|
|
if (slotIndex === 1) return this._skill2;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 绑定 battle(七十二变 roll 用 rng) */
|
|
|
|
|
bindBattle(battle: ProjectBattleHandle | null): void {
|
|
|
|
|
this._battle = battle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** skill 展示变化时回调(重新绑定拖拽/点击) */
|
|
|
|
|
setOnDisplayChanged(handler: (() => void) | null): void {
|
|
|
|
|
this._onDisplayChanged = handler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 绑定战斗 skill 描述浮层 */
|
|
|
|
|
setOwnedDesc(desc: DailyShopOwnedDesc | null): void {
|
|
|
|
|
this._ownedDesc = desc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 刷新 skill1 / skill2 描述点击(拖拽槽由 Drag 处理轻触) */
|
|
|
|
|
bindSkillDesc(): void {
|
|
|
|
|
const desc = this._ownedDesc;
|
|
|
|
|
if (!desc) return;
|
|
|
|
|
for (let slotIndex = 0; slotIndex < 2; slotIndex++) {
|
|
|
|
|
const slot = this.getSlot(slotIndex);
|
|
|
|
|
const root = slot?.root;
|
|
|
|
|
if (!root?.isValid || !root.active) continue;
|
|
|
|
|
const parsed = slot.getCastParsed();
|
|
|
|
|
if (!parsed) continue;
|
|
|
|
|
if (slot.hasBattleSkillInteraction() && slot.isDraggableSkill()) {
|
|
|
|
|
desc.unbindBattleSkillItem(root);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
desc.bindBattleSkillItem(root, parsed, DailyShopKind.Active);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 按主动槽位顺序刷新展示 */
|
|
|
|
|
refresh(): void {
|
|
|
|
|
this._ownedDesc?.hide();
|
|
|
|
|
const host = getBattleGameRandomActiveSkillHost();
|
|
|
|
|
const slotIds = this._activeSlotIds();
|
|
|
|
|
for (let slotIndex = 0; slotIndex < 2; slotIndex++) {
|
|
|
|
|
const baseId = Number(slotIds[slotIndex]) || 0;
|
|
|
|
|
const baseParsed = this._resolveActiveBuff(baseId);
|
|
|
|
|
const row = (baseParsed?.row as ITableBattlebuff) ?? null;
|
|
|
|
|
host.syncSlot(slotIndex, baseId, row);
|
|
|
|
|
this._applySlotDisplay(slotIndex, baseParsed);
|
|
|
|
|
}
|
|
|
|
|
this.bindSkillDesc();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 本局主动 id(最多 2 槽) */
|
|
|
|
|
private _activeSlotIds(): number[] {
|
|
|
|
|
const actives = BattleBuffCarryStore.getActives(this._battle?.localPlayerId ?? 0);
|
|
|
|
|
return [actives[0]?.id ?? 0, actives[1]?.id ?? 0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 帧更新 CD;七十二变 CD 结束自动 roll */
|
|
|
|
|
tick(dt: number): void {
|
|
|
|
|
const host = getBattleGameRandomActiveSkillHost();
|
|
|
|
|
for (let slotIndex = 0; slotIndex < 2; slotIndex++) {
|
|
|
|
|
const slot = this.getSlot(slotIndex);
|
|
|
|
|
if (!slot) continue;
|
|
|
|
|
const wasOnCd = !slot.isReady();
|
|
|
|
|
slot.tick(dt);
|
|
|
|
|
if (!host.isRandomActiveSlot(slotIndex) || host.isShowingRolledBuff(slotIndex)) continue;
|
|
|
|
|
if (!wasOnCd || !slot.isReady()) continue;
|
|
|
|
|
if (!host.tryRollOnCooldownReady(slotIndex, this._battle)) continue;
|
|
|
|
|
const baseId = Number(this._activeSlotIds()[slotIndex]) || 0;
|
|
|
|
|
this._applySlotDisplay(slotIndex, this._resolveActiveBuff(baseId), { ready: true });
|
|
|
|
|
this.bindSkillDesc();
|
|
|
|
|
this._onDisplayChanged?.();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 指定槽位开始冷却(0=skill1,1=skill2) */
|
|
|
|
|
startCooldown(slotIndex: number): void {
|
|
|
|
|
const host = getBattleGameRandomActiveSkillHost();
|
|
|
|
|
if (host.isShowingRolledBuff(slotIndex)) {
|
|
|
|
|
host.onRolledBuffConsumed(slotIndex);
|
|
|
|
|
const baseId = Number(this._activeSlotIds()[slotIndex]) || 0;
|
|
|
|
|
const baseParsed = this._resolveActiveBuff(baseId);
|
|
|
|
|
this._applySlotDisplay(slotIndex, baseParsed);
|
|
|
|
|
this.getSlot(slotIndex)?.startCooldown();
|
|
|
|
|
this._onDisplayChanged?.();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (slotIndex === 0) {
|
|
|
|
|
this._skill1?.startCooldown();
|
|
|
|
|
} else if (slotIndex === 1) {
|
|
|
|
|
this._skill2?.startCooldown();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 刷新单槽展示 */
|
|
|
|
|
private _applySlotDisplay(
|
|
|
|
|
slotIndex: number,
|
|
|
|
|
baseParsed: DailyShopParsed | null,
|
|
|
|
|
opts?: { ready?: boolean },
|
|
|
|
|
): void {
|
|
|
|
|
const slot = this.getSlot(slotIndex);
|
|
|
|
|
if (!slot) return;
|
|
|
|
|
const host = getBattleGameRandomActiveSkillHost();
|
|
|
|
|
const castParsed = host.resolveDisplayParsed(slotIndex, baseParsed);
|
|
|
|
|
if (!castParsed) {
|
|
|
|
|
slot.bind(null);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const bindOpts: BattleGameSkillSlotBindOpts = {
|
|
|
|
|
carriedParsed: baseParsed,
|
|
|
|
|
castParsed,
|
|
|
|
|
};
|
|
|
|
|
if (host.isShowingRolledBuff(slotIndex) || opts?.ready) {
|
|
|
|
|
bindOpts.cdLeftSec = 0;
|
|
|
|
|
bindOpts.cdTotalSec = 0;
|
|
|
|
|
}
|
|
|
|
|
slot.bind(castParsed, bindOpts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 槽位 id → 主动 buff 解析结果 */
|
|
|
|
|
private _resolveActiveBuff(slotId: number): DailyShopParsed | null {
|
|
|
|
|
const id = Number(slotId) || 0;
|
|
|
|
|
if (id <= 0) return null;
|
|
|
|
|
const actives = BattleBuffCarryStore.getActives(this._battle?.localPlayerId ?? 0);
|
|
|
|
|
for (let i = 0; i < actives.length; i++) {
|
|
|
|
|
if (actives[i].id === id) return actives[i];
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|