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/battle/view/BagDragVisualPresenter.ts

201 lines
7.0 KiB

import { Node, UIOpacity } from 'cc';
import { GEvent } from 'db://assets/mx/module/event/GEvent';
import type { CocosBattleMonsterPresenter } from './CocosBattleMonsterPresenter';
import { BATTLE_MONSTER_CCCLASS } from './IBattleMonsterView';
/** 拖动期间怪物目标不透明度(须能看清路线) */
export const BAG_DRAG_MONSTER_OPACITY = 160;
/** 拖动期间战斗特效目标不透明度(接近不可见) */
export const BAG_DRAG_FX_OPACITY = 28;
/** 怪物渐显/落地收尾时:若处于背包拖动则使用拖动透明度 */
export function resolveBagDragMonsterOpacity(fullOpacity = 255): number {
const battle = gg.game.CurentBattle;
if (!battle?.isBagDragSlowActive) return fullOpacity;
return Math.round(fullOpacity * BAG_DRAG_MONSTER_OPACITY / 255);
}
/**
* 背包/地图拖动期间的战斗画面弱化:特效几乎隐藏,怪物半透明但仍可辨认。
* 与 BattleRuntime.beginBagDragSlowMo / forceEndBagDragSlowMo 成对。
*/
export class BagDragVisualPresenter {
private _flyLayer: Node | null = null;
private _scoreLayer: Node | null = null;
private _monsterPresenter: CocosBattleMonsterPresenter | null = null;
private _active = false;
private readonly _savedMonsterOpacity = new Map<Node, number>();
private readonly _savedFxNodeOpacity = new Map<Node, number>();
private _flyLayerOpacity: UIOpacity | null = null;
private _savedFlyOpacity = 255;
private _scoreLayerOpacity: UIOpacity | null = null;
private _savedScoreOpacity = 255;
private _refreshToken = 0;
bind(opts: {
flyLayer: Node | null;
scoreLayer: Node | null;
monsterPresenter: CocosBattleMonsterPresenter | null;
}): void {
this.unbind();
this._flyLayer = opts.flyLayer?.isValid ? opts.flyLayer : null;
this._scoreLayer = opts.scoreLayer?.isValid ? opts.scoreLayer : null;
this._monsterPresenter = opts.monsterPresenter;
GEvent.Ins.on(GEvent.BagDragVisualChanged, this._onBagDragVisualChanged, this);
}
unbind(): void {
GEvent.Ins.off(GEvent.BagDragVisualChanged, this._onBagDragVisualChanged, this);
this._stopRefresh();
this._restoreAll();
this._active = false;
this._flyLayer = null;
this._scoreLayer = null;
this._monsterPresenter = null;
}
private _onBagDragVisualChanged = (active: boolean): void => {
if (active) this._begin();
else this._end();
};
private _begin(): void {
if (this._active) return;
this._active = true;
this._applyAll();
this._startRefresh();
}
private _end(): void {
if (!this._active) return;
this._active = false;
this._stopRefresh();
this._restoreAll();
}
private _applyAll(): void {
this._applyMonsterNodes();
this._applyMechanismFxNodes();
this._applyFlyLayer();
this._applyScoreLayer();
}
private _applyMonsterNodes(): void {
const map = this._monsterPresenter?.getEntityNodeMap();
if (!map) return;
for (const node of map.values()) {
this._dimMonsterNode(node);
}
}
private _applyMechanismFxNodes(): void {
const root = this._monsterPresenter?.monsterRoot;
if (!root?.isValid) return;
for (const child of root.children) {
if (!child?.isValid) continue;
if (child.getComponent(BATTLE_MONSTER_CCCLASS)) continue;
this._dimFxNode(child);
}
}
private _dimMonsterNode(node: Node | null | undefined): void {
if (!node?.isValid) return;
if (node.opacity <= 0) return;
if (!this._savedMonsterOpacity.has(node)) {
this._savedMonsterOpacity.set(node, 255);
}
node.opacity = BAG_DRAG_MONSTER_OPACITY;
}
private _dimFxNode(node: Node | null | undefined): void {
if (!node?.isValid) return;
if (!this._savedFxNodeOpacity.has(node)) {
this._savedFxNodeOpacity.set(node, node.opacity);
}
node.opacity = BAG_DRAG_FX_OPACITY;
}
private _applyFlyLayer(): void {
const layer = this._flyLayer;
if (!layer?.isValid) return;
// 禁止给 ui_fly 整层 UIOpacity:背包拉线 ghost 也挂在此层,会被压成几乎看不见。
// 只弱化非 ghost 宿主的直接子节点。
const layerUo = layer.getComponent(UIOpacity);
if (layerUo?.isValid && layerUo.opacity < 255) {
layerUo.opacity = 255;
}
this._flyLayerOpacity = null;
this._savedFlyOpacity = 255;
for (let i = 0; i < layer.children.length; i++) {
const child = layer.children[i];
if (!child?.isValid) continue;
if (child.name === '__bagDragGhostHost') continue;
this._dimFxNode(child);
}
}
private _applyScoreLayer(): void {
const layer = this._scoreLayer;
if (!layer?.isValid) return;
const uo = layer.getComponent(UIOpacity) ?? layer.addComponent(UIOpacity);
if (!this._scoreLayerOpacity) {
this._savedScoreOpacity = uo.opacity;
}
this._scoreLayerOpacity = uo;
uo.opacity = BAG_DRAG_FX_OPACITY;
}
private _restoreAll(): void {
this._savedMonsterOpacity.clear();
const map = this._monsterPresenter?.getEntityNodeMap();
if (map) {
map.forEach((node, entityId) => {
this._monsterPresenter?.ensureMarchVisible(entityId, node);
});
}
for (const [node, opacity] of this._savedFxNodeOpacity) {
if (node?.isValid) node.opacity = opacity;
}
this._savedFxNodeOpacity.clear();
if (this._flyLayerOpacity?.isValid) {
this._flyLayerOpacity.opacity = this._savedFlyOpacity;
}
this._flyLayerOpacity = null;
this._savedFlyOpacity = 255;
// 拖动弱化期间可能给 ui_fly 整层加过 UIOpacity,收尾时强制拉回
if (this._flyLayer?.isValid) {
const layerUo = this._flyLayer.getComponent(UIOpacity);
if (layerUo?.isValid && layerUo.opacity < 255) {
layerUo.opacity = 255;
}
}
if (this._scoreLayerOpacity?.isValid) {
this._scoreLayerOpacity.opacity = this._savedScoreOpacity;
}
this._scoreLayerOpacity = null;
this._savedScoreOpacity = 255;
}
private _startRefresh(): void {
this._refreshToken++;
this._scheduleRefreshTick(this._refreshToken);
}
private _stopRefresh(): void {
this._refreshToken++;
}
private _scheduleRefreshTick(token: number): void {
const session = gg.game.CurentBattle?.session;
if (!session || token !== this._refreshToken || !this._active) return;
session.clock.schedule(0.3, () => {
if (token !== this._refreshToken || !this._active) return;
this._applyMonsterNodes();
this._applyMechanismFxNodes();
this._scheduleRefreshTick(token);
});
}
}