Skip to content

Commit

Permalink
feat: add main menu
Browse files Browse the repository at this point in the history
  • Loading branch information
LiprikON2 committed Jul 1, 2023
1 parent 11eb110 commit c122e21
Show file tree
Hide file tree
Showing 15 changed files with 269 additions and 119 deletions.
40 changes: 28 additions & 12 deletions client/src/game/scripts/core/game.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "phaser";
import Phaser from "phaser";
import MouseWheelScrollerPlugin from "phaser3-rex-plugins/plugins/mousewheelscroller-plugin.js";
import RotateToPlugin from "phaser3-rex-plugins/plugins/rotateto-plugin.js";
import SoundFadePlugin from "phaser3-rex-plugins/plugins/soundfade-plugin.js";
Expand All @@ -22,7 +23,7 @@ const DEFAULT_HEIGHT = 1080;
// const WIDTH = Math.round(Math.max(width, height) * DPR);
// const HEIGHT = Math.round(Math.min(width, height) * DPR);

export const gameConfig = {
export const gameConfig: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO,
transparent: true,
scale: {
Expand Down Expand Up @@ -80,25 +81,40 @@ export const gameConfig = {
},
};

class Game {
export class Game {
config;
game;
game: Phaser.Game;

constructor(config) {
this.config = config;
}

init = (settings = {}) => {
this.game = new Phaser.Game(this.config);
this.game.settings = settings;
};
init = async (settings) => {
const whenIsBooted = new Promise((resolve) => {
this.game = new Phaser.Game({
...this.config,
callbacks: { postBoot: () => resolve(true) },
});
this.game["settings"] = settings;
});
await whenIsBooted;

getScene = (): MainScene => {
return this.game?.scene?.keys?.MainScene ?? null;
};
const whenSceneCreated = new Promise((resolve) => {
const MainScene = this.game.scene.keys.MainScene as MainScene;
MainScene.events.on("create", resolve);
});
await whenSceneCreated;

getPlayer = (): Spaceship | null => {
return this.getScene()?.player ?? null;
return this;
};
get scene(): MainScene | null {
return (this.game?.scene?.keys?.MainScene as MainScene) ?? null;
}
get player(): Spaceship | null {
return this.scene?.player ?? null;
}
destroy = () => {
this.game.destroy(false);
};
}

Expand Down
64 changes: 40 additions & 24 deletions client/src/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import React, { useEffect } from "react";
import { Group } from "@mantine/core";
import styled from "@emotion/styled";

import { game } from "~/game";
import { syncSettingsToSession, useGame } from "./hooks";
import { TopLeft } from "./scenes/TopLeft";
import { TopRight } from "./scenes/TopRight";
import { Center } from "./scenes/Center";
import { Right } from "./scenes/Right";
import { BottomLeft } from "./scenes/BottomLeft";
import { BottomRight } from "./scenes/BottomRight";
import { Right } from "./scenes/Right";
import { syncSettingsToSession, useSettings } from "./hooks";

const StyledUI = styled.div`
position: absolute;
Expand All @@ -22,14 +22,14 @@ const StyledUI = styled.div`
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(8, 1fr);
grid-template-areas:
"top-l top-l top-l . . . . . . . top-r top-r"
" . . . . . . . . . . . ."
" . . . . . . . . . . . ."
" . . . . . . . . . right right right"
" . . . . . . . . . right right right"
" . . . . . . . . . . . ."
" . . . . . . . . . . . ."
"bot-l bot-l bot-l . . . . . . . bot-r bot-r";
"top-l top-l top-l . . . . . . . top-r top-r"
" . . . . . . . . . . . ."
" . . . . cent cent cent cent . . . ."
" . . . . cent cent cent cent . right right right"
" . . . . cent cent cent cent . right right right"
" . . . . cent cent cent cent . . . ."
" . . . . . . . . . . . ."
"bot-l bot-l bot-l . . . . . . . bot-r bot-r";
& > * {
margin: 1rem;
Expand All @@ -41,12 +41,31 @@ const StyledTopLeftGroup = styled(Group)`
grid-area: top-l;
justify-self: start;
align-self: start;
display: flex;
flex-wrap: nowrap;
`;
const StyledTopRightGroup = styled(Group)`
grid-area: top-r;
justify-self: end;
align-self: start;
`;
const StyledCenterGroup = styled(Group)`
grid-area: cent;
justify-self: stretch;
align-self: center;
display: flex;
flex-direction: column;
& > {
flex-grow: 1;
}
`;
const StyledRightGroup = styled(Group)`
grid-area: right;
justify-self: end;
align-self: center;
`;
const StyledBottomLeftGroup = styled(Group)`
grid-area: bot-l;
justify-self: start;
Expand All @@ -57,29 +76,26 @@ const StyledBottomRightGroup = styled(Group)`
justify-self: end;
align-self: end;
`;
const StyledRightGroup = styled(Group)`
grid-area: right;
justify-self: end;
align-self: center;
`;

export const App = () => {
const { settings } = useSettings();

useEffect(() => {
game.init(settings);
const unsub = syncSettingsToSession();

return unsub;
}, []);

const {
mode,
computed: { isLoaded },
} = useGame();

return (
<StyledUI>
<TopLeft GroupComponent={StyledTopLeftGroup} />
<TopRight GroupComponent={StyledTopRightGroup} />
<BottomLeft GroupComponent={StyledBottomLeftGroup} />
<BottomRight GroupComponent={StyledBottomRightGroup} />
{isLoaded && <TopLeft GroupComponent={StyledTopLeftGroup} />}
{isLoaded && <TopRight GroupComponent={StyledTopRightGroup} />}
{mode === "mainMenu" && <Center GroupComponent={StyledCenterGroup} />}
<Right GroupComponent={StyledRightGroup} />
{isLoaded && <BottomLeft GroupComponent={StyledBottomLeftGroup} />}
<BottomRight GroupComponent={StyledBottomRightGroup} />
</StyledUI>
);
};
1 change: 1 addition & 0 deletions client/src/ui/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./useGame";
export * from "./useSettings";
80 changes: 80 additions & 0 deletions client/src/ui/hooks/useGame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { MainScene } from "~/scenes";
import type { Spaceship } from "~/objects";
import { produce } from "immer";
import { create } from "zustand";

import { game } from "~/game/core/game";
import type { Game } from "~/game/core/game";

interface GameStore {
game: Game | null;
mode: "mainMenu" | "singleplayer" | "multiplayer";
computed: {
isLoading: boolean;
isLoaded: boolean;
player: Spaceship | null;
scene: MainScene | null;
};
loadSingleplayer: (settings) => Promise<void>;
loadMultiplayer: (settings) => Promise<void>;
loadMainMenu: () => void;
}

export const useGame = create<GameStore>((set, get) => ({
game: null,
mode: "mainMenu",

// https://github.com/pmndrs/zustand/issues/132
computed: {
get isLoading() {
return get().mode !== "mainMenu" && !get().computed.isLoaded;
},
get isLoaded() {
return !!get().game?.player;
},

get player() {
return get().game?.player;
},
get scene() {
return get().game?.scene;
},
},

loadSingleplayer: async (settings) => {
set(
produce((state) => {
state.mode = "singleplayer";
})
);
const singleplayerGame = await game.init(settings);

set(
produce((state) => {
state.game = singleplayerGame;
})
);
},
loadMultiplayer: async (settings) => {
set(
produce((state) => {
state.mode = "multiplayer";
})
);
const multiplayerGame = await game.init(settings);
set(
produce((state) => {
state.game = multiplayerGame;
})
);
},
loadMainMenu: () => {
get().game.destroy();
set(
produce((state) => {
state.game = null;
state.mode = "mainMenu";
})
);
},
}));
1 change: 1 addition & 0 deletions client/src/ui/hooks/useSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const defaultSettings = {
musicVolume: 0.05,
graphicsSettings: 1,
enableTouchControls: isTouchDevice(),
showDeviceInfo: false,
};

const key = "settings";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import React, { useState } from "react";
import { Divider, Drawer, Title } from "@mantine/core";
import { Drawer, Title } from "@mantine/core";
import { useDidUpdate, useSetState } from "@mantine/hooks";
import { DndContext, DragOverlay } from "@dnd-kit/core";

import { game } from "~/game";
import { InventorySection } from "./scenes/InventorySection";
import { InventoryItem } from "./scenes/InventorySection/scenes/ItemSlot/components/InventoryItem";
import { useGame } from "~/ui/hooks";

export const OutfittingDrawer = ({ shouldBeOpened, close }) => {
const [didLoad, setDidLoad] = useState(false);
const {
computed: { player },
} = useGame();

useDidUpdate(() => {
if (!didLoad) {
const player = game.getPlayer();
const activeOutfit = player?.outfitting.getOutfit();
// TODO: to not mutate state
const activeOutfit = player.outfitting.getOutfit();
if (activeOutfit) {
setDidLoad(() => true);
setOutfit(activeOutfit);
Expand Down Expand Up @@ -81,9 +84,9 @@ export const OutfittingDrawer = ({ shouldBeOpened, close }) => {
};

const reoutfit = () => {
const player = game.getPlayer();
const activeOutfit = player?.outfitting.getOutfit();
if (player && activeOutfit) {
if (activeOutfit) {
// TODO: to not mutate state
player.outfitting.reoutfit(outfit);
}
};
Expand Down
20 changes: 15 additions & 5 deletions client/src/ui/scenes/BottomRight/BottomRight.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import React from "react";
import { useToggle } from "@mantine/hooks";
import { User } from "tabler-icons-react";
import { Home, User } from "tabler-icons-react";

import { game } from "~/game";
import { Button } from "~/ui/components";
import { ProfileModal } from "./scenes/ProfileModal";
import { useGame } from "~/ui/hooks";

export const BottomRight = ({ GroupComponent }) => {
const {
loadMainMenu,
computed: { isLoaded, player },
} = useGame();
const [openedProfileModal, toggleProfileModal] = useToggle([false, true]);

const toggleProfile = () => {
const player = game.getPlayer();
if (player) {
toggleProfileModal();
toggleProfileModal();
if (isLoaded) {
// todo this will enable you to shoot and move in dying animation
player.active = openedProfileModal;
}
};

const handleLoadMainMenu = () => {
loadMainMenu();
};

return (
<>
<GroupComponent>
<Button isSquare onClick={handleLoadMainMenu}>
<Home />
</Button>
<Button isSquare onClick={toggleProfile}>
<User />
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@ import React, { useState } from "react";
import { Button, Group, Stack, Text } from "@mantine/core";
import { useSessionStorage } from "@mantine/hooks";

import { game } from "~/game";
import { NonFieldErrors } from "../../components";
import { useSaveMutation } from "./hooks";
import { useProfile } from "../../hooks";
import { useGame } from "~/ui/hooks";

export const Me = ({ onLogout }) => {
const { me, meStatus } = useProfile();
const {
computed: { isLoaded, player },
} = useGame();

const [accessToken, setAccessToken] = useSessionStorage({
key: "accessToken",
defaultValue: "",
});

const handleSave = () => {
const player = game.getPlayer();
if (player && accessToken) {
if (isLoaded && accessToken) {
const { x, y } = player;
save(me.id, { x, y }, accessToken);
}
};
const handleLoad = () => {
const player = game.getPlayer();

if (player) {
if (isLoaded) {
player.respawn(me.x, me.y);
player.followText.setText(me.username);
}
Expand Down
Loading

0 comments on commit c122e21

Please sign in to comment.