101f595bac
Add userData players/teams, scene npcTokens with hex-inscribed sizing, session scale synced to presentation, and Playwright e2e coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
72 lines
2.3 KiB
TypeScript
72 lines
2.3 KiB
TypeScript
import type { AppPlayer, AppPlayerTeam, PlayerTeamId } from '../types/appPlayers';
|
|
import { asPlayerTeamId } from '../types/ids';
|
|
import { DEFAULT_PLAYER_RING_COLOR, normalizeAppPlayerTeam } from '../types/appPlayers';
|
|
import { normalizeHexColor } from '../npcs/npcGroups';
|
|
|
|
/** Имя команды уникально среди плоского списка (без вложенности). */
|
|
export function uniquePlayerTeamName(
|
|
base: string,
|
|
teams: readonly AppPlayerTeam[],
|
|
exceptId?: PlayerTeamId | null,
|
|
): string {
|
|
const root = base.trim() || 'Team';
|
|
const used = new Set(
|
|
teams.filter((t) => t.id !== exceptId).map((t) => t.name.trim().toLowerCase()),
|
|
);
|
|
if (!used.has(root.toLowerCase())) return root;
|
|
let i = 2;
|
|
while (used.has(`${root.toLowerCase()} (${String(i)})`)) i += 1;
|
|
return `${root} (${String(i)})`;
|
|
}
|
|
|
|
export function normalizeAppPlayerTeams(raw: unknown): AppPlayerTeam[] {
|
|
if (!Array.isArray(raw)) return [];
|
|
const out: AppPlayerTeam[] = [];
|
|
const ids = new Set<string>();
|
|
for (const item of raw) {
|
|
const t = normalizeAppPlayerTeam(item);
|
|
if (!t || ids.has(t.id)) continue;
|
|
ids.add(t.id);
|
|
out.push(t);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
export function assignPlayerToTeam(
|
|
players: readonly AppPlayer[],
|
|
playerId: string,
|
|
teamId: PlayerTeamId | null,
|
|
teamIds: Set<string>,
|
|
): AppPlayer[] {
|
|
return players.map((p) => {
|
|
if (p.id !== playerId) return p;
|
|
if (teamId && !teamIds.has(teamId)) return { ...p, teamId: null };
|
|
return { ...p, teamId };
|
|
});
|
|
}
|
|
|
|
/** Удаление команды: игроки этой команды становятся без команды. */
|
|
export function deletePlayerTeam(
|
|
teams: readonly AppPlayerTeam[],
|
|
players: readonly AppPlayer[],
|
|
teamId: PlayerTeamId,
|
|
): { teams: AppPlayerTeam[]; players: AppPlayer[] } {
|
|
return {
|
|
teams: teams.filter((t) => t.id !== teamId),
|
|
players: players.map((p) => (p.teamId === teamId ? { ...p, teamId: null } : p)),
|
|
};
|
|
}
|
|
|
|
export function createPlayerTeamDraft(
|
|
name: string,
|
|
color: string | undefined,
|
|
teams: readonly AppPlayerTeam[],
|
|
): AppPlayerTeam {
|
|
const id = asPlayerTeamId(`pteam_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`);
|
|
return {
|
|
id,
|
|
name: uniquePlayerTeamName(name, teams),
|
|
color: normalizeHexColor(color, DEFAULT_PLAYER_RING_COLOR),
|
|
};
|
|
}
|