Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 80 additions & 67 deletions src/pages/games/SnackGame/game/SnackGameBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import useModal from '@hooks/useModal';

import ProvocationSender from './components/ProvocationSender';
import { SnackGameDefaultResponse, SnackGameVerify } from './game.type';
import { GameHandlers, ItemHandlers } from './handlers.type';
import initializeApplication from './hook/initializeApplication';
import { PausePopup } from './popup/PausePopup';
import { RulePopup } from './popup/RulePopup';
Expand Down Expand Up @@ -49,48 +50,10 @@ const SnackGameBase = ({ replaceErrorHandler }: Props) => {
const guestMutation = useGuest();
const queryClient = useQueryClient();

// TODO: 훅 안으로 끌고 들어가기
const initializeAppScreens = async (application: SnackgameApplication) => {
application.appScreenPool.insert(
[SettingsPopup, () => new SettingsPopup(application, handleGameResume)],
[RulePopup, () => new RulePopup(application)],
[
PausePopup,
() => new PausePopup(application, handleGameResume, handleGameEnd),
],
[
LobbyScreen,
() =>
new LobbyScreen(application, handleSetMode, handleNonLoggedInUser),
],
[
GameScreen,
() =>
new GameScreen(
application,
handleGetMode,
handleStreak,
handleGameStart,
handleBomb,
handleFever,
handleGamePause,
handleGameEnd,
fetchUserItem,
),
],
);
return application.appScreenPool;
};
const application = initializeApplication({
canvasBaseRef,
initializeAppScreens,
});

const handleApplicationError = () => {
application.show(LobbyScreen);
};

// 게임 진행 관련 functions
let session: SnackGameDefaultResponse | undefined;
let sessionMode: string | undefined;
let cumulativeStreaks: StreakWithMeta[] = [];
Expand All @@ -107,49 +70,28 @@ const SnackGameBase = ({ replaceErrorHandler }: Props) => {
return data;
};

// TODO: 모드를 타입으로 정의해도 괜찮을 것 같습니다
const handleGameStart = async () => {
session = await gameStart();
return session;
};

// TODO: 현재 PixiJS 컨테이너와 게임의 모든 것이 결합되어있는데,
// 이것을 게임 상태(모드, 점수, 스트릭) 및 게임 규칙을 관리하는 순수한 스낵게임 클래스로 분리하면 좋겠네요.
// 지금 아래에 있는 getMode, handleStreak 같은 단순 상태를 가져오는 메서드들을 축약하고 싶어요!

// TODO: 모드를 타입으로 정의해도 괜찮을 것 같습니다
const handleGetMode = () => sessionMode!;
const handleSetMode = (mode: string) => {
sessionMode = mode;
};

// TODO: 지금은 인자로 숫자를 사용하지만, '스트릭' VO를 만들어 사용하면 더 좋겠네요.
const handleStreak = async (streak: StreakWithMeta, isGolden: boolean) => {
cumulativeStreaks = [...cumulativeStreaks, streak];

if (isGolden) {
session = await handleStreaksMove();
}
return session!;
};

const handleBomb = async (position: SnackGamePosition, isGolden: boolean) => {
if (isGolden) {
session = await handleStreaksMove();
}
session = await triggerBomb(session!.sessionId, position);
return session;
};

const handleFever = async () => {
session = await triggerFever(session!.sessionId);
return session;
};

const handleStreaksMove = async (): Promise<SnackGameVerify> => {
const result = await verifyStreaks(session!.sessionId, cumulativeStreaks);
cumulativeStreaks = [];
return result;
};

// ==================== 게임 핸들러 ====================
const handleGameStart = async () => {
session = await gameStart();
return session;
};

const handleGamePause = async () => {
if (!session || session.state === 'PAUSED') return;
if (cumulativeStreaks.length > 0) {
Expand Down Expand Up @@ -186,6 +128,77 @@ const SnackGameBase = ({ replaceErrorHandler }: Props) => {
});
};

// TODO: 지금은 인자로 숫자를 사용하지만, '스트릭' VO를 만들어 사용하면 더 좋겠네요.
const handleStreak = async (streak: StreakWithMeta, isGolden: boolean) => {
cumulativeStreaks = [...cumulativeStreaks, streak];

if (isGolden) {
session = await handleStreaksMove();
}
return session!;
};

// ==================== 아이템 핸들러 ====================
const handleBomb = async (position: SnackGamePosition, isGolden: boolean) => {
if (isGolden) {
session = await handleStreaksMove();
}
session = await triggerBomb(session!.sessionId, position);
return session;
};

const handleFever = async () => {
session = await triggerFever(session!.sessionId);
return session;
};

// ==================== 게임/아이템 핸들러 그룹핑 객체 ====================
const gameHandlers: GameHandlers = {
start: handleGameStart,
pause: handleGamePause,
resume: handleGameResume,
end: handleGameEnd,
streak: handleStreak,
};

const itemHandlers: ItemHandlers = {
bomb: handleBomb,
fever: handleFever,
};

// TODO: 훅 안으로 끌고 들어가기
const initializeAppScreens = async (application: SnackgameApplication) => {
application.appScreenPool.insert(
[SettingsPopup, () => new SettingsPopup(application, handleGameResume)],
[RulePopup, () => new RulePopup(application)],
[
PausePopup,
() => new PausePopup(application, handleGameResume, handleGameEnd),
],
[
LobbyScreen,
() =>
new LobbyScreen(application, handleSetMode, handleNonLoggedInUser),
],
[
GameScreen,
() =>
new GameScreen(
application,
handleGetMode,
gameHandlers,
itemHandlers,
fetchUserItem,
),
],
);
return application.appScreenPool;
};
const application = initializeApplication({
canvasBaseRef,
initializeAppScreens,
});

const handleGameResultClose = async () => {
const isGuest =
JSON.parse(window.localStorage.getItem(ATOM_KEY.USER_PERSIST) || '{}')
Expand Down
36 changes: 36 additions & 0 deletions src/pages/games/SnackGame/game/handlers.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {
SnackGameBizStart,
SnackGameBizVerify,
} from '@pages/games/SnackgameBiz/game.type';

import {
SnackGameDefaultResponse,
SnackGameStart,
SnackGameVerify,
} from './game.type';
import { SnackGamePosition, StreakWithMeta } from './snackGame/SnackGameUtil';

/**
* 게임 핸들러 타입
*/
export interface GameHandlers {
start: () => Promise<SnackGameStart | SnackGameBizStart>;
pause: () => Promise<void>;
resume: () => Promise<void>;
end: () => Promise<void>;
streak: (
streak: StreakWithMeta,
isGolden: boolean,
) => Promise<SnackGameVerify | SnackGameBizVerify>;
}

/**
* 아이템 핸들러 타입
*/
export interface ItemHandlers {
bomb: (
position: SnackGamePosition,
isGolden: boolean,
) => Promise<SnackGameDefaultResponse>;
fever: () => Promise<SnackGameDefaultResponse>;
}
37 changes: 9 additions & 28 deletions src/pages/games/SnackGame/game/screen/GameScreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,11 @@ import gsap from 'gsap';
import { Container, Rectangle, Ticker } from 'pixi.js';

import { ItemBar } from '@pages/games/SnackGame/game/ui/ItemBar';
import {
SnackGameBizStart,
SnackGameBizVerify,
} from '@pages/games/SnackgameBiz/game.type';
import { ItemResponse } from '@utils/types/item.type';

import { AppScreen, AppScreenConstructor } from './appScreen';
import { SnackgameApplication } from './SnackgameApplication';
import {
SnackGameDefaultResponse,
SnackGameStart,
SnackGameVerify,
} from '../game.type';
import { GameHandlers, ItemHandlers } from '../handlers.type';
import { PausePopup } from '../popup/PausePopup';
import { SettingsPopup } from '../popup/SettingPopup';
import { Snack } from '../snackGame/Snack';
Expand All @@ -23,7 +15,6 @@ import {
SnackGameMode,
SnackGamePosition,
Streak,
StreakWithMeta,
snackGameGetConfig,
} from '../snackGame/SnackGameUtil';
import { BeforeGameStart } from '../ui/BeforeGameStart';
Expand Down Expand Up @@ -66,18 +57,8 @@ export class GameScreen extends Container implements AppScreen {
constructor(
private app: SnackgameApplication,
private getCurrentMode: () => string,
private handleStreak: (
streak: StreakWithMeta,
isGolden: boolean,
) => Promise<SnackGameVerify | SnackGameBizVerify>,
private handleGameStart: () => Promise<SnackGameStart | SnackGameBizStart>,
private handleBomb: (
position: SnackGamePosition,
isGolden: boolean,
) => Promise<SnackGameDefaultResponse>,
private handleFever: () => Promise<SnackGameDefaultResponse>,
private handleGamePause: () => Promise<void>,
private handleGameEnd: () => Promise<void>,
private gameHandlers: GameHandlers,
private itemHandlers: ItemHandlers,
private fetchUserItem: () => Promise<{ items: ItemResponse[] }>,
) {
super();
Expand Down Expand Up @@ -126,7 +107,7 @@ export class GameScreen extends Container implements AppScreen {
return acc;
}, []);

return this.handleStreak(
return this.gameHandlers.streak(
{ coordinates: streak, isFever, occurredAt },
isGolden,
);
Expand All @@ -140,7 +121,7 @@ export class GameScreen extends Container implements AppScreen {
if (snack.type === 2) isGolden = true;
});

return this.handleBomb(position, isGolden);
return this.itemHandlers.bomb(position, isGolden);
};
this.snackGame.onSnackGameBoardReset =
this.onSnackGameBoardReset.bind(this);
Expand Down Expand Up @@ -169,7 +150,7 @@ export class GameScreen extends Container implements AppScreen {
onUse: async (type) => {
this.snackGame.setSelectedItem(type);
if (type === 'FEVER_TIME') {
await this.handleFever();
await this.itemHandlers.fever();
this.feverTimer.start(30, () => {
this.snackGame.setSelectedItem(null);
});
Expand All @@ -179,7 +160,7 @@ export class GameScreen extends Container implements AppScreen {
}),
);

const { board } = await this.handleGameStart();
const { board } = await this.gameHandlers.start();
const mode = this.getCurrentMode() as SnackGameMode;
const snackGameConfig = snackGameGetConfig({
rows: board.length,
Expand Down Expand Up @@ -225,7 +206,7 @@ export class GameScreen extends Container implements AppScreen {

public async onPause(popup?: AppScreenConstructor) {
if (this.snackGame.isPlaying()) {
await this.handleGamePause();
await this.gameHandlers.pause();
this.gameContainer.interactiveChildren = false;
this.snackGame.pause();
if (this.feverTimer.isRunning()) {
Expand Down Expand Up @@ -316,7 +297,7 @@ export class GameScreen extends Container implements AppScreen {
this.finished = true;
this.snackGame.stopPlaying();

await this.handleGameEnd();
await this.gameHandlers.end();
} catch (error) {
this.app.setError(error);
}
Expand Down
Loading