Newer
Older
PixelPaintWar / apps / client / src / scenes / game / hooks / useGameSceneController.ts
/**
 * useGameSceneController
 * ゲーム画面の状態管理と GameManager 連携を担うフック
 * Pixi描画領域,残り時間表示,入力橋渡しを提供する
 */
import { useCallback, useEffect, useRef, useState } from "react";
import { config } from "@client/config";
import { GameInputManager } from "@client/scenes/game/GameInputManager";
import { GameManager } from "@client/scenes/game/GameManager";

const formatRemainingTime = (remaining: number) => {
  const mins = Math.floor(remaining / 60);
  const secs = Math.floor(remaining % 60);
  return `${mins}:${secs.toString().padStart(2, "0")}`;
};

const getInitialTimeDisplay = () =>
  formatRemainingTime(config.GAME_CONFIG.GAME_DURATION_SEC);

/** ゲーム画面の状態と入力ハンドラを提供するフック */
export const useGameSceneController = (myId: string | null) => {
  const pixiContainerRef = useRef<HTMLDivElement>(null);
  const gameManagerRef = useRef<GameManager | null>(null);
  const inputManagerRef = useRef<GameInputManager | null>(null);
  const [timeLeft, setTimeLeft] = useState(getInitialTimeDisplay());
  const [startCountdownText, setStartCountdownText] = useState<string | null>(
    null,
  );
  const [isInputEnabled, setIsInputEnabled] = useState(false);

  useEffect(() => {
    if (!pixiContainerRef.current || !myId) return;

    const manager = new GameManager(pixiContainerRef.current, myId);
    manager.init();

    gameManagerRef.current = manager;
    inputManagerRef.current = new GameInputManager(
      (x, y) => {
        manager.setJoystickInput(x, y);
      },
      () => {
        return manager.placeBomb() !== null;
      },
    );

    const timerInterval = setInterval(() => {
      const nextDisplay = formatRemainingTime(manager.getRemainingTime());
      setTimeLeft((prev) => (prev === nextDisplay ? prev : nextDisplay));

      const remainingSec = manager.getStartCountdownSec();
      const nextCountdown = remainingSec > 0 ? String(remainingSec) : null;
      setStartCountdownText((prev) =>
        prev === nextCountdown ? prev : nextCountdown,
      );

      const nextInputEnabled = manager.isInputEnabled();
      setIsInputEnabled((prev) =>
        prev === nextInputEnabled ? prev : nextInputEnabled,
      );
    }, config.GAME_CONFIG.TIMER_DISPLAY_UPDATE_MS);

    return () => {
      manager.destroy();
      gameManagerRef.current = null;
      inputManagerRef.current = null;
      clearInterval(timerInterval);
      setStartCountdownText(null);
      setIsInputEnabled(false);
    };
  }, [myId]);

  const handleInput = useCallback((x: number, y: number) => {
    inputManagerRef.current?.handleJoystickInput(x, y);
  }, []);

  const handlePlaceBomb = useCallback((): boolean => {
    return inputManagerRef.current?.handlePlaceBomb() ?? false;
  }, []);

  return {
    pixiContainerRef,
    timeLeft,
    startCountdownText,
    isInputEnabled,
    handleInput,
    handlePlaceBomb,
  };
};