Files
DndGamePlayer/app/shared/players/playerTeams.ts
T
Ivan Fontosh 101f595bac feat(players): app Players library and circular NPC tokens on scenes
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>
2026-07-30 13:38:35 +08:00

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),
};
}