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.
258 lines
9.3 KiB
258 lines
9.3 KiB
import type { EntityId } from 'db://assets/mx/module/ecs';
|
|
import { TeamComp, type BattleSession } from 'db://assets/mx/module/battle';
|
|
import type { ProjectBattleHandle } from '../runtime/BattleRuntime';
|
|
import { findCompositionForEntity, findGridIndexForEntity } from '../runtime/DeployEntityQuery';
|
|
import type { Player } from '../../game/Player';
|
|
import type { CocosBagBoardView } from '../view/CocosBagBoardView';
|
|
|
|
/** 降级后按新等级重绑战斗(由 BattleRuntime 注入,避免 Host 回引 CombatSetup) */
|
|
export interface ILevelDownCombatRefresh {
|
|
/**
|
|
* @param session 战斗会话
|
|
* @param entityId 塔实体
|
|
* @param level 新合成等级
|
|
* @param player 所属玩家
|
|
*/
|
|
(session: BattleSession, entityId: EntityId, level: number, player: Player): void;
|
|
}
|
|
|
|
/** 一次限时降级 */
|
|
interface LevelDownEntry {
|
|
/** 被降级实体 */
|
|
entity: EntityId;
|
|
/** 字块所属玩家 */
|
|
playerId: number;
|
|
/** 当前格 */
|
|
gridIndex: number;
|
|
/** 施加前等级 */
|
|
originLevel: number;
|
|
/** 实际降低级数 */
|
|
applied: number;
|
|
/** 剩余秒 */
|
|
remain: number;
|
|
}
|
|
|
|
let _host: TowerLevelDownHost | null = null;
|
|
|
|
/** 龟丞相 atk_buff 类型7:限时降级 */
|
|
export function getTowerLevelDownHost(): TowerLevelDownHost {
|
|
if (!_host) _host = new TowerLevelDownHost();
|
|
return _host;
|
|
}
|
|
|
|
/**
|
|
* 非组合字限时降级:最低 1 级,期间禁合成升级、可移动;到期恢复。
|
|
* 表现只改等级字色,不加框。
|
|
*/
|
|
export class TowerLevelDownHost {
|
|
private readonly _entries: LevelDownEntry[] = [];
|
|
private _bagView: CocosBagBoardView | null = null;
|
|
/** 降级后刷新战斗;未绑定时只改合成等级 */
|
|
private _refreshCombat: ILevelDownCombatRefresh | null = null;
|
|
|
|
/**
|
|
* 绑定棋盘视图(刷等级字色)。
|
|
* @param view 本机棋盘
|
|
*/
|
|
bindBagView(view: CocosBagBoardView | null): void {
|
|
this._bagView = view;
|
|
}
|
|
|
|
/**
|
|
* 绑定降级后的战斗刷新。
|
|
* @param fn 刷新函数;传 null 清除
|
|
*/
|
|
bindCombatRefresh(fn: ILevelDownCombatRefresh | null): void {
|
|
this._refreshCombat = fn;
|
|
}
|
|
|
|
/**
|
|
* 施加或刷新降级。
|
|
* @param battle 战斗句柄
|
|
* @param target 目标塔
|
|
* @param delta 降低级数
|
|
* @param durationSec 持续时间
|
|
*/
|
|
apply(battle: ProjectBattleHandle, target: EntityId, delta: number, durationSec: number): void {
|
|
const d = Math.max(1, Math.floor(Number(delta) || 1));
|
|
const dur = Number(durationSec) || 0;
|
|
if (!(dur > 0)) {
|
|
console.log('[TowerLevelDown] atk_buff 类型7缺少持续时间', target, durationSec);
|
|
this._dropOnce(battle, target, d);
|
|
return;
|
|
}
|
|
const session = battle.session;
|
|
if (!session.world.isAlive(target)) return;
|
|
const existing = this._find(target);
|
|
if (existing) {
|
|
existing.remain = dur;
|
|
this._syncGrid(battle, existing);
|
|
this._setFx(existing.gridIndex, true);
|
|
return;
|
|
}
|
|
const team = session.world.getComponent(target, TeamComp);
|
|
const playerId = Math.floor(Number(team?.ownerId) || 0);
|
|
const player = battle.getPlayer(playerId);
|
|
const map = battle.Map;
|
|
if (!player || !map) return;
|
|
const comp = findCompositionForEntity(target, player, session, map);
|
|
if (!comp || comp.gridIndices.length > 1) return;
|
|
const gridIndex = findGridIndexForEntity(target, player, session, map);
|
|
if (gridIndex == null) return;
|
|
const origin = Math.max(1, comp.level);
|
|
const next = Math.max(1, origin - d);
|
|
const applied = origin - next;
|
|
if (applied > 0 && !this._setLevel(battle, playerId, gridIndex, next, target)) return;
|
|
this._entries.push({ entity: target, playerId, gridIndex, originLevel: origin, applied, remain: dur });
|
|
this._setFx(gridIndex, true);
|
|
}
|
|
|
|
/**
|
|
* 该格是否处于降级禁合成。
|
|
* @param playerId 玩家
|
|
* @param gridIndex 地图格
|
|
*/
|
|
isGridBlocked(playerId: number, gridIndex: number): boolean {
|
|
for (let i = 0; i < this._entries.length; i++) {
|
|
const e = this._entries[i];
|
|
if (e.playerId === playerId && e.gridIndex === gridIndex) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 推进计时并跟随位移。
|
|
* @param battle 战斗句柄
|
|
* @param dt 逻辑秒
|
|
*/
|
|
tick(battle: ProjectBattleHandle, dt: number): void {
|
|
if (this._entries.length <= 0) return;
|
|
for (let i = this._entries.length - 1; i >= 0; i--) {
|
|
const e = this._entries[i];
|
|
this._syncGrid(battle, e);
|
|
if (!battle.session.world.isAlive(e.entity)) {
|
|
this._end(battle, i, false);
|
|
continue;
|
|
}
|
|
e.remain -= dt;
|
|
if (e.remain <= 0) this._end(battle, i, true);
|
|
else this._setFx(e.gridIndex, true);
|
|
}
|
|
}
|
|
|
|
/** 战斗结束:清字色,不回等级 */
|
|
clear(): void {
|
|
for (let i = this._entries.length - 1; i >= 0; i--) {
|
|
this._setFx(this._entries[i].gridIndex, false);
|
|
}
|
|
this._entries.length = 0;
|
|
this._bagView = null;
|
|
}
|
|
|
|
/**
|
|
* 无持续时间:只降一次级。
|
|
* @param battle 战斗句柄
|
|
* @param target 目标
|
|
* @param delta 降低级数
|
|
*/
|
|
private _dropOnce(battle: ProjectBattleHandle, target: EntityId, delta: number): void {
|
|
const session = battle.session;
|
|
if (!session.world.isAlive(target)) return;
|
|
const team = session.world.getComponent(target, TeamComp);
|
|
const playerId = Math.floor(Number(team?.ownerId) || 0);
|
|
const player = battle.getPlayer(playerId);
|
|
const map = battle.Map;
|
|
if (!player || !map) return;
|
|
const comp = findCompositionForEntity(target, player, session, map);
|
|
if (!comp || comp.gridIndices.length > 1) return;
|
|
const gridIndex = findGridIndexForEntity(target, player, session, map);
|
|
if (gridIndex == null) return;
|
|
const next = Math.max(1, comp.level - delta);
|
|
if (next === comp.level) return;
|
|
this._setLevel(battle, playerId, gridIndex, next, target);
|
|
}
|
|
|
|
/**
|
|
* 同步实体当前格并迁移字色。
|
|
* @param battle 战斗句柄
|
|
* @param e 运行态
|
|
*/
|
|
private _syncGrid(battle: ProjectBattleHandle, e: LevelDownEntry): void {
|
|
const player = battle.getPlayer(e.playerId);
|
|
const map = battle.Map;
|
|
if (!player || !map || !battle.session.world.isAlive(e.entity)) return;
|
|
const live = findGridIndexForEntity(e.entity, player, battle.session, map);
|
|
if (live == null || live === e.gridIndex) return;
|
|
this._setFx(e.gridIndex, false);
|
|
e.gridIndex = live;
|
|
this._setFx(live, true);
|
|
}
|
|
|
|
/**
|
|
* 结束一条:可选恢复等级。
|
|
* @param battle 战斗句柄
|
|
* @param index 下标
|
|
* @param restore 是否加回等级
|
|
*/
|
|
private _end(battle: ProjectBattleHandle, index: number, restore: boolean): void {
|
|
const e = this._entries[index];
|
|
this._entries.splice(index, 1);
|
|
this._setFx(e.gridIndex, false);
|
|
if (!restore || e.applied <= 0) return;
|
|
if (!battle.session.world.isAlive(e.entity)) return;
|
|
const player = battle.getPlayer(e.playerId);
|
|
const map = battle.Map;
|
|
if (!player || !map) return;
|
|
const grid = findGridIndexForEntity(e.entity, player, battle.session, map) ?? e.gridIndex;
|
|
const comp = findCompositionForEntity(e.entity, player, battle.session, map);
|
|
const cur = comp?.level ?? 0;
|
|
if (!(cur > 0)) return;
|
|
this._setLevel(battle, e.playerId, grid, cur + e.applied, e.entity);
|
|
}
|
|
|
|
/**
|
|
* 改合成等级并刷新战斗属性。
|
|
* @param battle 战斗句柄
|
|
* @param playerId 玩家
|
|
* @param gridIndex 格
|
|
* @param newLevel 目标等级
|
|
* @param entity 实体
|
|
*/
|
|
private _setLevel(battle: ProjectBattleHandle, playerId: number, gridIndex: number, newLevel: number, entity: EntityId): boolean {
|
|
const player = battle.getPlayer(playerId);
|
|
const bag = player?.bagSession;
|
|
if (!bag) return false;
|
|
const view = this._bagView;
|
|
view?.setSuppressUpgradeFx(true);
|
|
const ok = bag.merge.setCompositionLevelAtGrid(gridIndex, newLevel);
|
|
view?.setSuppressUpgradeFx(false);
|
|
if (!ok) return false;
|
|
if (!battle.session.world.isAlive(entity)) return true;
|
|
if (!this._refreshCombat) {
|
|
console.warn('[TowerLevelDown] 战斗刷新未绑定', entity);
|
|
return true;
|
|
}
|
|
this._refreshCombat(battle.session, entity, newLevel, player);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 按实体查找。
|
|
* @param entity 实体
|
|
*/
|
|
private _find(entity: EntityId): LevelDownEntry | null {
|
|
for (let i = 0; i < this._entries.length; i++) {
|
|
if (this._entries[i].entity === entity) return this._entries[i];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 开关格子降级字色。
|
|
* @param gridIndex 格
|
|
* @param on 是否降级中
|
|
*/
|
|
private _setFx(gridIndex: number, on: boolean): void {
|
|
this._bagView?.getMapGrids().get(gridIndex)?.setLevelDownStyle(on);
|
|
}
|
|
}
|
|
|