Newer
Older
PixelPaintWar / apps / client / src / scenes / game / application / AppearanceResolver.ts
/**
 * AppearanceResolver
 * teamId から見た目リソースを解決する責務を集約する
 * プレイヤー画像,塗り色,unknown のフォールバック規約を統一する
 */
import { config as sharedConfig } from "@repo/shared";
import { config } from "@client/config";

/** チーム由来の見た目解決を提供するリゾルバー */
export class AppearanceResolver {
  private static readonly TEAM_CHARACTER_IMAGE_FILES = [
    "red.svg",
    "blue.svg",
    "green.svg",
    "yellow.svg",
  ] as const;

  private readonly cachedTeamColors: number[];

  constructor() {
    this.cachedTeamColors = config.GAME_CONFIG.TEAM_COLORS.map((colorCode) => this.parseColorCode(colorCode));
  }

  /** teamId からプレイヤー画像ファイル名を解決する */
  public resolvePlayerImageFile(teamId: number): string {
    const { TEAM_COUNT } = config.GAME_CONFIG;
    const imageFiles = AppearanceResolver.TEAM_CHARACTER_IMAGE_FILES;

    config.validateTeamConfig();

    if (imageFiles.length !== TEAM_COUNT) {
      throw new Error(
        `GAME_CONFIG mismatch: imageFiles length (${imageFiles.length}) must equal TEAM_COUNT (${TEAM_COUNT})`,
      );
    }

    if (!this.isKnownTeamId(teamId)) {
      return imageFiles[0];
    }

    return imageFiles[teamId] ?? imageFiles[0];
  }

  /** teamId からチーム色を解決する */
  public resolveTeamColor(teamId: number): number {
    if (!this.isKnownTeamId(teamId)) {
      return config.GAME_CONFIG.MAP_GRID_COLOR;
    }

    const teamColor = this.cachedTeamColors[teamId];
    if (!Number.isInteger(teamColor)) {
      return config.GAME_CONFIG.MAP_GRID_COLOR;
    }

    return teamColor;
  }

  /** teamId からマップセル色を解決する,unknown は未塗り扱いにする */
  public resolveMapCellColor(teamId: number): number | null {
    if (teamId === sharedConfig.UNKNOWN_TEAM_ID) {
      return null;
    }

    return this.resolveTeamColor(teamId);
  }

  private isKnownTeamId(teamId: number): boolean {
    return Number.isInteger(teamId)
      && teamId >= 0
      && teamId < config.GAME_CONFIG.TEAM_COUNT;
  }

  private parseColorCode(colorCode: string): number {
    const normalizedColorCode = colorCode.startsWith("#")
      ? colorCode.slice(1)
      : colorCode;

    const parsedColor = Number.parseInt(normalizedColorCode, 16);
    if (Number.isNaN(parsedColor)) {
      return config.GAME_CONFIG.MAP_GRID_COLOR;
    }

    return parsedColor;
  }
}