feat(scripts): RoomSceneStart.ts + pure layout helpers + tests (setPlayerCount/setSeatLayout/setPlayerData/clearSeats API)
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user