leaky-ships/leaky-ships/hooks/useGameProps.ts

239 lines
6.7 KiB
TypeScript

import {
ActionDispatchProps,
EventBarModes,
Hit,
MouseCursor,
ShipProps,
Target,
TargetPreview,
} from "../interfaces/frontend"
import { GameSettings } from "@components/Lobby/SettingsFrame/Setting"
import { getPayloadwithChecksum } from "@lib/getPayloadwithChecksum"
import { socket } from "@lib/socket"
import {
initlialMouseCursor,
initlialTarget,
initlialTargetPreview,
} from "@lib/utils/helpers"
import {
GamePropsSchema,
optionalGamePropsSchema,
PlayerSchema,
} from "@lib/zodSchemas"
import { produce } from "immer"
import { SetStateAction } from "react"
import { toast } from "react-toastify"
import { create } from "zustand"
import { devtools } from "zustand/middleware"
const initialState: optionalGamePropsSchema & {
userStates: {
isReady: boolean
isConnected: boolean
}[]
menu: keyof EventBarModes
mode: number
ships: ShipProps[]
hits: Hit[]
target: Target
targetPreview: TargetPreview
mouseCursor: MouseCursor
} = {
menu: "actions",
mode: 0,
payload: null,
hash: null,
ships: [],
hits: [],
target: initlialTarget,
targetPreview: initlialTargetPreview,
mouseCursor: initlialMouseCursor,
userStates: Array.from(Array(2), () => ({
isReady: false,
isConnected: false,
})),
}
export type State = typeof initialState
export type Action = {
DispatchAction: (props: ActionDispatchProps) => void
setTarget: (target: SetStateAction<Target>) => void
setTargetPreview: (targetPreview: SetStateAction<TargetPreview>) => void
setMouseCursor: (mouseCursor: SetStateAction<MouseCursor>) => void
setPlayer: (payload: { users: PlayerSchema[] }) => string | null
setSetting: (settings: GameSettings) => string | null
full: (newProps: GamePropsSchema) => void
leave: (cb: () => void) => void
setIsReady: (payload: { i: number; isReady: boolean }) => void
starting: () => void
addShip: (props: ShipProps) => void
removeShip: (props: ShipProps) => void
setIsConnected: (payload: { i: number; isConnected: boolean }) => void
reset: () => void
}
export const useGameProps = create<State & Action>()(
devtools(
(set) => ({
...initialState,
DispatchAction: (action) =>
set(
produce((state: State) => {
// switch (action.type) {
// case "fireMissile":
// case "htorpedo":
// case "vtorpedo": {
// state.hits.push(...action.payload)
// }
// }
})
),
setTarget: (dispatch) =>
set(
produce((state: State) => {
if (typeof dispatch === "function")
state.target = dispatch(state.target)
else state.target = dispatch
})
),
setTargetPreview: (dispatch) =>
set(
produce((state: State) => {
if (typeof dispatch === "function")
state.targetPreview = dispatch(state.targetPreview)
else state.targetPreview = dispatch
})
),
setMouseCursor: (dispatch) =>
set(
produce((state: State) => {
if (typeof dispatch === "function")
state.mouseCursor = dispatch(state.mouseCursor)
else state.mouseCursor = dispatch
})
),
addShip: (props) =>
set(
produce((state: State) => {
state.ships.push(props)
})
),
removeShip: ({ size, variant, x, y }) =>
set(
produce((state: State) => {
const indexToRemove = state.ships.findIndex(
(ship) =>
ship.size === size &&
ship.variant === variant &&
ship.x === x &&
ship.y === y
)
state.ships.splice(indexToRemove, 1)
})
),
setPlayer: (payload) => {
let hash: string | null = null
set(
produce((state: State) => {
if (!state.payload) return
state.payload.users = payload.users
const body = getPayloadwithChecksum(state.payload)
if (!body.hash) {
toast.warn("Something is wrong... ", {
toastId: "st_wrong",
theme: "colored",
})
return
}
hash = body.hash
state.hash = hash
})
)
return hash
},
setSetting: (settings) => {
let hash: string | null = null
set(
produce((state: State) => {
if (!state.payload?.game) return
Object.assign(state.payload.game, settings)
const body = getPayloadwithChecksum(state.payload)
if (!body.hash) {
toast.warn("Something is wrong... ", {
toastId: "st_wrong",
theme: "colored",
})
return
}
hash = body.hash
state.hash = hash
})
)
return hash
},
full: (newGameProps) =>
set((state) => {
if (state.hash === newGameProps.hash) {
console.log("Everything up to date.")
} else {
console.log("Update was needed.", state.hash, newGameProps.hash)
if (
state.payload?.game?.id &&
state.payload?.game?.id !== newGameProps.payload?.game?.id
) {
console.warn(
"Different gameId detected on update: ",
state.payload?.game?.id,
newGameProps.payload?.game?.id
)
}
return newGameProps
}
return state
}),
leave: (cb) => {
socket.emit("leave", (ack) => {
if (!ack) {
toast.error("Something is wrong...")
}
cb()
})
},
setIsReady: ({ i, isReady }) =>
set(
produce((state: State) => {
state.userStates[i].isReady = isReady
state.userStates[i].isConnected = true
})
),
starting: () =>
set(
produce((state: State) => {
if (state.payload?.game?.state !== "lobby") return
state.payload.game.state = "starting"
state.userStates = state.userStates.map((e) => ({
...e,
isReady: false,
}))
})
),
setIsConnected: ({ i, isConnected }) =>
set(
produce((state: State) => {
state.userStates[i].isConnected = isConnected
if (isConnected) return
state.userStates[i].isReady = false
})
),
reset: () => {
set(initialState)
},
}),
{
name: "gameState",
}
)
)