214 lines
5.7 KiB
JavaScript
214 lines
5.7 KiB
JavaScript
/**
|
|
* @fileoverview React component for GameBase.jsx
|
|
*
|
|
* @module components.game.GameBase
|
|
*/
|
|
|
|
|
|
import React from "react";
|
|
import PropTypes from "prop-types";
|
|
import { Panel, PanelGroup } from "react-resizable-panels";
|
|
import GameNavBar from "./GameNavBar";
|
|
import GameFaseInfo from "./GameFaseInfo";
|
|
import GameArea from "./GameArea";
|
|
import GameFooter from "./GameFooter";
|
|
import SeletorDeFases from "./SeletorDeFases";
|
|
import SucessoModal from "./SucessoModal";
|
|
import FalhaModal from "./FalhaModal";
|
|
import ResizeHandle from "./ResizeHandle";
|
|
import { useIsMobile } from "../../hooks/useIsMobile";
|
|
import { useGameState } from "../../contexts/GameStateContext";
|
|
import { EditorProvider } from "../../contexts/EditorContext";
|
|
import { usePhaser } from "../../hooks/usePhaser";
|
|
import { useGameModals } from "../../hooks/useGameModals";
|
|
|
|
function GameBaseContent({
|
|
gameFactory,
|
|
gameConfig,
|
|
children,
|
|
onHelpClick,
|
|
customFailureHandler,
|
|
}) {
|
|
const isMobile = useIsMobile();
|
|
|
|
const {
|
|
currentPhase,
|
|
setCurrentPhase,
|
|
resetProgress,
|
|
executionState,
|
|
generatedCode,
|
|
failureMessage,
|
|
restart,
|
|
setOnWorkspaceChange,
|
|
} = useGameState();
|
|
|
|
const phaseConfig = gameConfig.fases[currentPhase - 1];
|
|
const usaModalFalha = !!customFailureHandler;
|
|
|
|
const { gameContainerRef } = usePhaser({
|
|
gameFactory,
|
|
phaseConfig,
|
|
currentPhase,
|
|
customFailureHandler,
|
|
gameConfig,
|
|
});
|
|
|
|
const {
|
|
modalFasesAberto,
|
|
setModalFasesAberto,
|
|
modalSucessoAberto,
|
|
modalFalhaAberto,
|
|
blocksRemainingCount,
|
|
handleProximaFase,
|
|
handleFecharModalSucesso,
|
|
handleFecharModalFalha,
|
|
handleTentarNovamente,
|
|
} = useGameModals({
|
|
executionState,
|
|
currentPhase,
|
|
phaseConfig,
|
|
gameConfig,
|
|
setCurrentPhase,
|
|
restart,
|
|
setOnWorkspaceChange,
|
|
usaModalFalha,
|
|
});
|
|
|
|
const handleResetProgresso = () => {
|
|
resetProgress();
|
|
window.dispatchEvent(
|
|
new CustomEvent("resetBlocklyWorkspace", {
|
|
detail: { gameId: gameConfig.gameId },
|
|
}),
|
|
);
|
|
};
|
|
|
|
const codeToShow = React.useMemo(() => {
|
|
if (!generatedCode) return "Nenhum código gerado";
|
|
|
|
let codigo = "";
|
|
|
|
if (typeof generatedCode === "string") {
|
|
codigo = generatedCode;
|
|
} else if (typeof generatedCode === "object" && generatedCode.codigo) {
|
|
codigo = generatedCode.codigo;
|
|
} else {
|
|
return "Código não disponível";
|
|
}
|
|
|
|
const codigoLimpo = codigo
|
|
.split("\n")
|
|
.filter((linha) => !linha.trim().startsWith("highlightBlock("))
|
|
.join("\n")
|
|
.trim();
|
|
|
|
return codigoLimpo || codigo;
|
|
}, [generatedCode]);
|
|
|
|
return (
|
|
<div className="game-base-page flex flex-col h-screen w-screen">
|
|
<GameNavBar
|
|
title={`${gameConfig.gameName}`}
|
|
type={`${gameConfig.type}`}
|
|
thumbnail={gameConfig.thumbnail}
|
|
icon={gameConfig.icon}
|
|
/>
|
|
<GameFaseInfo phaseData={phaseConfig} phaseNumber={currentPhase} />
|
|
<div className="flex-1 min-h-0 flex flex-col">
|
|
<PanelGroup
|
|
direction={isMobile ? "vertical" : "horizontal"}
|
|
className="h-full w-full"
|
|
>
|
|
<Panel defaultSize={isMobile ? 48 : 48} minSize={isMobile ? 10 : 10}>
|
|
<EditorProvider gameConfig={gameConfig} currentPhase={currentPhase}>
|
|
{children}
|
|
</EditorProvider>
|
|
</Panel>
|
|
<ResizeHandle direction={isMobile ? "vertical" : "horizontal"} />
|
|
<Panel defaultSize={isMobile ? 49 : 49} minSize={isMobile ? 10 : 10}>
|
|
<GameArea blocksRemaining={blocksRemainingCount} phaseId={currentPhase}>
|
|
<div ref={gameContainerRef} className="w-full h-full" />
|
|
</GameArea>
|
|
</Panel>
|
|
</PanelGroup>
|
|
</div>
|
|
|
|
<GameFooter
|
|
gameConfig={gameConfig}
|
|
currentPhase={currentPhase}
|
|
onOpenPhaseSelector={() => setModalFasesAberto(true)}
|
|
onHelpClick={onHelpClick}
|
|
/>
|
|
|
|
<SeletorDeFases
|
|
isVisible={modalFasesAberto}
|
|
onClose={() => setModalFasesAberto(false)}
|
|
currentPhase={currentPhase}
|
|
gameConfig={gameConfig}
|
|
onChangePhase={(fase) => {
|
|
setCurrentPhase(fase, "manual_selector");
|
|
setModalFasesAberto(false);
|
|
}}
|
|
onResetProgress={handleResetProgresso}
|
|
/>
|
|
|
|
<SucessoModal
|
|
isOpen={modalSucessoAberto}
|
|
onClose={handleFecharModalSucesso}
|
|
onNextPhase={handleProximaFase}
|
|
generatedCode={codeToShow}
|
|
currentPhase={currentPhase}
|
|
totalPhases={gameConfig.fases.length}
|
|
canGoNext={currentPhase < gameConfig.fases.length}
|
|
/>
|
|
|
|
<FalhaModal
|
|
isOpen={modalFalhaAberto}
|
|
onClose={handleFecharModalFalha}
|
|
onRetry={handleTentarNovamente}
|
|
customMessage={failureMessage}
|
|
currentPhase={currentPhase}
|
|
generatedCode={codeToShow}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function GameBase({
|
|
gameFactory,
|
|
gameConfig,
|
|
children,
|
|
onHelpClick,
|
|
customFailureHandler,
|
|
}) {
|
|
return (
|
|
<GameBaseContent
|
|
gameFactory={gameFactory}
|
|
gameConfig={gameConfig}
|
|
onHelpClick={onHelpClick}
|
|
customFailureHandler={customFailureHandler}
|
|
>
|
|
{children}
|
|
</GameBaseContent>
|
|
);
|
|
}
|
|
|
|
GameBase.propTypes = {
|
|
gameFactory: PropTypes.func.isRequired,
|
|
gameConfig: PropTypes.shape({
|
|
gameId: PropTypes.string.isRequired,
|
|
gameName: PropTypes.string.isRequired,
|
|
fases: PropTypes.array.isRequired,
|
|
type: PropTypes.string,
|
|
thumbnail: PropTypes.string,
|
|
icon: PropTypes.string,
|
|
}).isRequired,
|
|
children: PropTypes.node.isRequired,
|
|
onHelpClick: PropTypes.func,
|
|
customFailureHandler: PropTypes.func,
|
|
helpHandler: PropTypes.func,
|
|
failureHandler: PropTypes.func,
|
|
onHelp: PropTypes.func,
|
|
title: PropTypes.string,
|
|
};
|