import type { EntityId } from 'db://assets/mx/module/ecs'; import type { BattleSession, PlayerSlot } from 'db://assets/mx/module/battle'; import type { ComposedUnit } from 'db://assets/mx/module/merge'; import type { BattleBagSession } from '../battle/bag/BattleBagSession'; import type { BattleDeployParams } from '../battle/types/ProjectBattleTypes'; import { TableNames } from './ConfigTableData'; import { heroUltSkillId } from '../battle/config/BattleHeroTable'; /** 玩家上下文可访问的对局句柄(避免 Player ↔ IBattleRuntimeHandle 循环依赖) */ export interface IPlayerMatch { readonly session: BattleSession; bagSession: BattleBagSession | null; deploy(params: BattleDeployParams): boolean; } /** * 项目层玩家上下文(PVP 兼容) * 每个 PlayerSlot 对应一个实例;闯关模式仅本地玩家 isLocal=true。 */ export class Player { readonly playerId: number; readonly isLocal: boolean; readonly match: IPlayerMatch; /** 技能表现控制器(由 SkillController 注入;此处不 import 该类以免 Player 拉战斗表现环) */ skillController: object | null = null; /** @deprecated 词条表已删,保留空数组兼容快照 */ readonly selectedSkills: ITableSkill[] = []; /** 非本机玩家可挂独立 bag(AI 无头背包) */ private _attachedBag: BattleBagSession | null = null; constructor(match: IPlayerMatch, playerId: number, isLocal: boolean) { this.match = match; this.playerId = playerId; this.isLocal = isLocal; } /** * 挂接独立背包会话(供 AI 等非本机玩家)。 * @param bag 无头 bag;传 null 解除 */ attachBagSession(bag: BattleBagSession | null): void { this._attachedBag = bag; } /** MX 玩家槽(金币、城墙、已部署单位) */ get slot(): PlayerSlot | undefined { return this.match.session.players[this.playerId]; } get deployedUnits(): readonly EntityId[] { return this.slot?.deployedUnits ?? []; } get gold(): number { return this.slot?.gold ?? 0; } get wallHp(): number { return this.slot?.wallHp ?? 0; } get wallMaxHp(): number { return this.slot?.wallMaxHp ?? 0; } /** 本机用 match.bagSession;AI 用 attachBagSession */ get bagSession(): BattleBagSession | null { if (this._attachedBag) return this._attachedBag; if (!this.isLocal) return null; return this.match.bagSession ?? null; } /** 地图上已合成的攻击单位 */ get compositions(): readonly ComposedUnit[] { return this.bagSession?.compositions ?? []; } deploy(params: Omit): boolean { return this.match.deploy({ ...params, playerId: this.playerId }); } /** @deprecated 词条已删 */ addSelectedSkill(_skill: ITableSkill): boolean { return false; } /** * 英雄主动技能 id(按合成等级)。 * @param heroId Hero.id * @param composeLevel 合成等级,默认 1 */ getActiveSkillBaseAtkId(heroId: number, composeLevel = 1): number { const hero = gg.data.table.getTableData(TableNames.Hero, heroId); return heroUltSkillId(hero, composeLevel); } /** @deprecated 词条替换已删 */ replaceActiveSkill(_weapon1Id: number, _baseAtkId: number): void { /* no-op */ } hasActiveSkillOverride(_weapon1Id: number): boolean { return false; } exportActiveSkillOverrides(): Record { return {}; } get selectedSkillIds(): readonly number[] { return this.selectedSkills.map(s => s.id); } }