import { _decorator, Component, Node } from 'cc'; import type { PlayerState } from '../framework/platform/stores/types.ts'; import { computeActiveFlags, computePositions, type SeatLayout } from './room-scene-layout.ts'; export { computeActiveFlags, computePositions }; export type { SeatLayout }; const { ccclass, property } = _decorator; /** * RoomSceneStart: 房间场景控制脚本。 * * 工作流: * 1. 子游戏初始化 → setPlayerCount(n) + setSeatLayout(layout) 调整可见槽位与位置 * 2. 子游戏收到玩家状态变更 → setPlayerData(seat, data) 注入到对应槽位的 PlayerInfoView * 3. 子游戏重置房间 → clearSeats() 把当前可见槽位刷成占位数据 * * 8 个 playerSlots 在场景里预先挂好(Plan Task 2 负责),setPlayerCount 只切 active, * 不运行时 instantiate 节点(避免预制重复实例化开销)。 * * 纯函数算法(computeActiveFlags / computePositions)放在 room-scene-layout.ts, * 本文件只 re-export — 这样单元测试可以直接 import 布局函数而无需 mock cc 模块 * (CLAUDE.md 第二准则:RoomSceneStart 的下游消费者不应被 cc 依赖拖累)。 */ @ccclass('RoomSceneStart') export class RoomSceneStart extends Component { @property([Node]) playerSlots: Node[] = []; private currentCount: number = 4; private currentLayout: SeatLayout = 'horizontal'; start(): void { if (!this.playerSlots || this.playerSlots.length !== 8) { throw new Error( `[RoomSceneStart] playerSlots must have 8 entries, got ${this.playerSlots?.length}`, ); } this.setPlayerCount(this.currentCount); this.setSeatLayout(this.currentLayout); } public setPlayerCount(n: number): void { if (n < 1 || n > this.playerSlots.length) { throw new Error( `[RoomSceneStart] setPlayerCount: n must be 1-${this.playerSlots.length}, got ${n}`, ); } this.currentCount = n; const flags = computeActiveFlags(n, this.playerSlots.length); for (let i = 0; i < this.playerSlots.length; i++) { const slot = this.playerSlots[i]; if (slot) slot.active = flags[i]; } } public setSeatLayout(layout: SeatLayout): void { this.currentLayout = layout; const positions = computePositions(layout, this.currentCount); for (let i = 0; i < this.currentCount && i < positions.length; i++) { const slot = this.playerSlots[i]; if (slot) slot.setPosition(positions[i].x, positions[i].y, 0); } } public setPlayerData(seat: number, data: PlayerState): void { if (seat < 1 || seat > this.currentCount) { throw new Error( `[RoomSceneStart] setPlayerData: seat ${seat} out of range 1-${this.currentCount}`, ); } const slot = this.playerSlots[seat - 1]; if (!slot) return; const view = slot.getComponent('PlayerInfoView') as any; if (view && typeof view.bindTo === 'function') { view.bindTo({ player: { state: { value: data } } }); } } public clearSeats(): void { const placeholder: PlayerState = { playerid: 0, nickname: '占位', avatar: '', sex: 0, bean: 0, roomcard: 0, score: 0, charm: 0, taskstate: 0, advanced: 0, bankpower: 0, bank: 0, bankpwd: 0, ip: '', sign: null, tel: null, invitecode: null, initCard: 0, initBean: 0, }; for (let i = 0; i < this.currentCount; i++) { this.setPlayerData(i + 1, placeholder); } } }