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
12 changes: 8 additions & 4 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ io.onConnection((channel) => {
const playerId = channel.id ?? raise("Channel ID is null"); // ID is assigned to player
store.dispatch(PlayerActions.connect(playerId));

if (store.getState().game.gameMode == null) {
if (store.getState().game.game == null) {
store.dispatch(
GameActions.assignGameMode({
gameMode: "FFA",
GameActions.setupGame({
game: {
gameMode: "FFA",
map: "testMap1",
currentTimeMs: 0, // TODO: Add this later when games can actually start and end https://app.clickup.com/t/86b5v1mym
},
}),
);
}
Expand All @@ -26,7 +30,7 @@ io.onConnection((channel) => {
team.playerIds.includes(playerId),
);
// In FFA, if the player disconnects, we don't want to keep their team
if (store.getState().game.gameMode === "FFA" && playersTeam) {
if (store.getState().game.game?.gameMode === "FFA" && playersTeam) {
store.dispatch(GameActions.deleteTeam({ teamId: playersTeam[0] }));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const GameScore: React.FC<GameScoreProps> = ({ gameStateRef }) => {
}
});

const gameMode = gameStateRef.current.game.gameMode;
const gameMode = gameStateRef.current.game.game?.gameMode;
if (gameMode == null) return null;

const liveTeams = gameStateRef.current.game.teams;
Expand Down
1 change: 0 additions & 1 deletion packages/frontend/src/game/World.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const World: React.FC = () => {
const gameStateRef: GameStateRef = React.useRef({
game: {
game: null,
gameMode: null,
teams: {},
},
player: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ interface UpdateClientGameStateProps {
}

export const updateClientGameState = ({ game, gameStateRef }: UpdateClientGameStateProps) => {
gameStateRef.current.game.gameMode = game.gameMode;
gameStateRef.current.game.teams = { ...game.teams };
if (game.game) {
gameStateRef.current.game.game = { ...game.game };
Expand Down
17 changes: 0 additions & 17 deletions packages/lib/src/engine/slices/game/gameSlice.types.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,37 +1,49 @@
import "immer";
import { assertNever } from "../../../utils/assertNever";
import { assertNever } from "../../utils/assertNever";
import { createSlice } from "@reduxjs/toolkit";
import { GAME_MODES } from "../../../objects/gameModes";
import { objectEntries } from "../../../utils/objectUtils";
import { raise } from "../../../utils/raise";
import type { GameSliceState } from "./gameSlice.types";
import { GAME_MODES } from "../../objects/gameModes";
import { MAP_CONFIGS } from "../../objects/mapConfigs";
import { objectEntries } from "../../utils/objectUtils";
import { raise } from "../../utils/raise";
import type { PayloadAction } from "@reduxjs/toolkit";
import type { WritableDraft } from "immer";

export interface GameTeamState {
name: string;
playerIds: string[];
currentScore: number;
}

export interface GameGameState {
map: keyof typeof MAP_CONFIGS;
gameMode: keyof typeof GAME_MODES;
currentTimeMs: number; // TODO: Add this later when games can actually start and end https://app.clickup.com/t/86b5v1mym
}

export interface GameSliceState {
game: GameGameState | null;
teams: Record<string, GameTeamState>;
}

type State = WritableDraft<GameSliceState>;
type Action<T> = PayloadAction<T>;

const initialState: GameSliceState = {
game: null,
gameMode: null,
teams: {},
};

type State = WritableDraft<GameSliceState>;
type Action<T> = PayloadAction<T>;

export const gameSlice = createSlice({
name: "game",
initialState,
reducers: {
assignGameMode: (state: State, action: Action<{ gameMode: keyof typeof GAME_MODES }>) => {
const { gameMode } = action.payload;
state.gameMode = gameMode;
},
assignPlayerToTeam: (state: State, action: Action<{ playerId: string }>) => {
const PRESET_TEAM_NAMES = ["Red", "Blue", "Green", "Yellow", "Purple", "Orange", "Black", "White", "Silver", "Gold"] as const; // prettier-ignore

const { playerId } = action.payload;
const existingTeams = objectEntries(state.teams);

if (state.gameMode == null) return raise("No gamemode has been assigned to the game yet");
if (state.game == null) return raise("No gamemode has been assigned to the game yet");

// Find used team names
const usedNames = new Set(existingTeams.map(([, team]) => team.name));
Expand All @@ -45,7 +57,7 @@ export const gameSlice = createSlice({
(a, b) => a[1].playerIds.length - b[1].playerIds.length,
);

const maxPlayersPerTeam = GAME_MODES[state.gameMode].maxNumberOfPlayersPerTeam;
const maxPlayersPerTeam = GAME_MODES[state.game.gameMode].maxNumberOfPlayersPerTeam;

if (teamsByLeastPlayers.length === 0 || teamsByLeastPlayers[0][1].playerIds.length >= maxPlayersPerTeam) {
const newTeamId = crypto.randomUUID();
Expand Down Expand Up @@ -77,6 +89,10 @@ export const gameSlice = createSlice({
// Remove the player from the team
teamEntry[1].playerIds = teamEntry[1].playerIds.filter((id) => id !== playerId);
},
setupGame: (state: State, action: Action<{ game: GameGameState }>) => {
const { game } = action.payload;
state.game = game;
},
updateCurrentScore: (
state: State,
action: Action<{ adjustmentType: "increase" | "decrease"; scoreAmount: number; teamId: string }>,
Expand Down
26 changes: 0 additions & 26 deletions packages/lib/src/engine/slices/player/playerSlice.types.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
import "immer";
import { createSlice } from "@reduxjs/toolkit";
import { damagePerShot, HitLocation } from "../../../functions/damagePerShot";
import { damagePerShot, HitLocation } from "../../functions/damagePerShot";
import { MAP_CONFIGS } from "../../objects/mapConfigs";
import { Quaternion, Vector3 } from "three";
import { SerializableThree } from "../../../utils/serializableThree";
import { WeaponActions, weaponSlice } from "../weapon/weaponSlice";
import type { PlayerSliceState, PlayerState } from "./playerSlice.types";
import { SerializableThree } from "../../utils/serializableThree";
import { WeaponActions, weaponSlice } from "./weaponSlice";
import type { PayloadAction } from "@reduxjs/toolkit";
import type { Vector3Like } from "three";
import type { QuaternionLike, Vector3Like } from "three";
import type { WritableDraft } from "immer";
import { MAP_CONFIGS } from "../../../objects/mapConfigs";
import type { Weapon } from "../../objects/weapon";

export type PlayerStance = "standing" | "crouching" | "prone";

export interface PlayerState {
canRise: boolean;
currentHealth: number;
currentWeapon: Weapon | null;
direction: QuaternionLike;
isAiming: boolean;
isAlive: boolean;
isJumping: boolean;
isReloading: boolean;
isShooting: boolean;
isSprinting: boolean;
isWalking: boolean;
position: Vector3Like;
stance: PlayerStance;
timeSinceLastDamage: number;
}

export interface PlayerSliceState {
bulletImpacts: Vector3Like[]; // TODO: Move this to another state slice e.g. "world" slice https://app.clickup.com/t/86b5k36rf
players: Record<string, PlayerState>;
}

const initialState: PlayerSliceState = {
bulletImpacts: [],
Expand Down
18 changes: 0 additions & 18 deletions packages/lib/src/engine/slices/weapon/weaponSlice.types.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import "immer";
import { createSlice } from "@reduxjs/toolkit";
import { objectEntries } from "../../../utils/objectUtils";
import { raise } from "../../../utils/raise";
import { WEAPONS } from "../../../objects/weapon";
import { objectEntries } from "../../utils/objectUtils";
import { raise } from "../../utils/raise";
import { WEAPONS } from "../../objects/weapon";
import type { PayloadAction } from "@reduxjs/toolkit";
import type { WeaponSliceState } from "./weaponSlice.types";
import type { QuaternionLike, Vector3Like } from "three";
import type { WritableDraft } from "immer";

type WeaponWithPlayer =
| { currentPlayerId: string; isActive: boolean }
| { currentPlayerId: null; direction: QuaternionLike; position: Vector3Like }; // If not with player, gun is on ground somewhere

export type WeaponState = {
attachments?: string[]; // TODO: Future feature https://app.clickup.com/t/86b5v1tv7
camo?: string; // TODO: Future feature https://app.clickup.com/t/86b5v1td1
currentAmmoInMagazine: number;
reserveAmmo: number;
weaponName: keyof typeof WEAPONS;
} & WeaponWithPlayer;

export interface WeaponSliceState {
weapons: Record<string, WeaponState>;
}

const initialState: WeaponSliceState = {
weapons: {},
};
Expand Down
6 changes: 3 additions & 3 deletions packages/lib/src/engine/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { configureStore } from "@reduxjs/toolkit";
import { gameSlice } from "./slices/game/gameSlice";
import { playerSlice } from "./slices/player/playerSlice";
import { weaponSlice } from "./slices/weapon/weaponSlice";
import { gameSlice } from "./slices/gameSlice";
import { playerSlice } from "./slices/playerSlice";
import { weaponSlice } from "./slices/weaponSlice";

type EngineStore = ReturnType<typeof configureEngineStore>;

Expand Down
8 changes: 5 additions & 3 deletions packages/lib/src/engine/thunks/shootEnemyThunk.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { GameActions } from "../slices/game/gameSlice";
import { GameActions } from "../slices/gameSlice";
import { GAME_MODES } from "../../objects/gameModes";
import { objectEntries } from "../../utils/objectUtils";
import { PlayerActions } from "../slices/player/playerSlice";
import { PlayerActions } from "../slices/playerSlice";
import { raise } from "../../utils/raise";
import type { EngineDispatch, EngineState } from "../store";
import type { HitLocation } from "../../functions/damagePerShot";
import type { Vector3Like } from "three";
Expand Down Expand Up @@ -30,7 +31,8 @@ export const shootEnemyThunk =

const [teamId] = shooterTeam;

const gameMode = state.game.gameMode;
const gameMode = state.game.game?.gameMode;
if (state.game.game == null) raise("Game state is null");
if (gameMode && "kill" in GAME_MODES[gameMode].scoreAdjustments) {
dispatch(
GameActions.updateCurrentScore({
Expand Down
12 changes: 6 additions & 6 deletions packages/lib/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
export { assertNever } from "./utils/assertNever";
export { configureEngineStore } from "./engine/store";
export { GameActions } from "./engine/slices/game/gameSlice";
export { GameActions } from "./engine/slices/gameSlice";
export { GAME_MODES, MAX_PLAYERS_ALLOWED_IN_GAME } from "./objects/gameModes";
export { getActiveWeaponId, getAllPlayerWeaponIds } from "./utils/getPlayerWeapons";
export { isKey, objectKeys, objectEntries, objectFromEntries, objectValues } from "./utils/objectUtils";
export { MAP_CONFIGS } from "./objects/mapConfigs";
export { Optional } from "./utils/optional";
export { PlayerActions } from "./engine/slices/player/playerSlice";
export { PlayerActions } from "./engine/slices/playerSlice";
export { raise } from "./utils/raise";
export { SerializableThree } from "./utils/serializableThree";
export { shootEnemyThunk } from "./engine/thunks/shootEnemyThunk";
export { THUNKS } from "./engine/thunks/thunks";
export { WEAPONS } from "./objects/weapon";
export { WeaponActions } from "./engine/slices/weapon/weaponSlice";
export { WeaponActions } from "./engine/slices/weaponSlice";
export type { EngineState, EngineDispatch } from "./engine/store";
export type { FromSerializableThreeRecursive } from "./utils/serializableThree";
export type { GameSliceState, GameTeamState } from "./engine/slices/game/gameSlice.types";
export type { GameSliceState, GameTeamState } from "./engine/slices/gameSlice";
export type { MapConfiguration, Spawn } from "./objects/mapConfigs";
export type { PlayerState, PlayerStance } from "./engine/slices/player/playerSlice.types";
export type { PlayerState, PlayerStance } from "./engine/slices/playerSlice";
export type { WeaponType, WeaponClass, FiringMode, DamageType, Recoil, WeaponStats, Weapon } from "./objects/weapon";
export type { WeaponState } from "./engine/slices/weapon/weaponSlice.types";
export type { WeaponState } from "./engine/slices/weaponSlice";
2 changes: 1 addition & 1 deletion packages/lib/src/utils/getPlayerWeapons.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WeaponState } from "../engine/slices/weapon/weaponSlice.types";
import { objectEntries } from "./objectUtils";
import type { WeaponState } from "../engine/slices/weaponSlice";

export const getActiveWeaponId = (playerId: string, weapons: Record<string, WeaponState>) => {
const activeWeapon = objectEntries(weapons).find(
Expand Down