Working game logic
This commit is contained in:
parent
e0e0f0a728
commit
0a6fd88733
27 changed files with 1458 additions and 718 deletions
|
@ -1,5 +1,6 @@
|
||||||
import { count } from "./Gamefield"
|
import { count } from "./Gamefield"
|
||||||
import { useGameProps } from "@hooks/useGameProps"
|
import { useGameProps } from "@hooks/useGameProps"
|
||||||
|
import useIndex from "@hooks/useIndex"
|
||||||
import useShips from "@hooks/useShips"
|
import useShips from "@hooks/useShips"
|
||||||
import {
|
import {
|
||||||
borderCN,
|
borderCN,
|
||||||
|
@ -22,12 +23,10 @@ type TilesType = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function BorderTiles() {
|
function BorderTiles() {
|
||||||
|
const { activeUser } = useIndex()
|
||||||
const {
|
const {
|
||||||
DispatchAction,
|
|
||||||
payload,
|
payload,
|
||||||
mode,
|
mode,
|
||||||
hits,
|
|
||||||
target,
|
|
||||||
targetPreview,
|
targetPreview,
|
||||||
mouseCursor,
|
mouseCursor,
|
||||||
setTarget,
|
setTarget,
|
||||||
|
@ -41,17 +40,18 @@ function BorderTiles() {
|
||||||
const list = targetList(targetPreview, mode)
|
const list = targetList(targetPreview, mode)
|
||||||
if (
|
if (
|
||||||
!isGameTile ||
|
!isGameTile ||
|
||||||
!list.filter(({ x, y }) => !isAlreadyHit(x, y, hits)).length
|
!list.filter(
|
||||||
|
({ x, y }) => !isAlreadyHit(x, y, activeUser?.hits ?? [])
|
||||||
|
).length
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
if (target.show && target.x == x && target.y == y) {
|
if (!overlapsWithAnyBorder(targetPreview, mode))
|
||||||
DispatchAction({
|
setTarget({
|
||||||
action: "missile",
|
show: true,
|
||||||
...target,
|
x,
|
||||||
|
y,
|
||||||
|
orientation: targetPreview.orientation,
|
||||||
})
|
})
|
||||||
setTarget((t) => ({ ...t, show: false }))
|
|
||||||
} else if (!overlapsWithAnyBorder(targetPreview, mode))
|
|
||||||
setTarget({ show: true, x, y })
|
|
||||||
} else if (
|
} else if (
|
||||||
payload?.game?.state === "starting" &&
|
payload?.game?.state === "starting" &&
|
||||||
targetPreview.show &&
|
targetPreview.show &&
|
||||||
|
@ -62,15 +62,13 @@ function BorderTiles() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
DispatchAction,
|
activeUser?.hits,
|
||||||
hits,
|
|
||||||
mode,
|
mode,
|
||||||
payload?.game?.state,
|
payload?.game?.state,
|
||||||
setMouseCursor,
|
setMouseCursor,
|
||||||
setShips,
|
setShips,
|
||||||
setTarget,
|
setTarget,
|
||||||
ships,
|
ships,
|
||||||
target,
|
|
||||||
targetPreview,
|
targetPreview,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,13 +7,14 @@ import {
|
||||||
faSquare4,
|
faSquare4,
|
||||||
} from "@fortawesome/pro-regular-svg-icons"
|
} from "@fortawesome/pro-regular-svg-icons"
|
||||||
import {
|
import {
|
||||||
faArrowRightFromBracket,
|
|
||||||
faBroomWide,
|
faBroomWide,
|
||||||
faCheck,
|
faCheck,
|
||||||
faComments,
|
faComments,
|
||||||
faEye,
|
faEye,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
|
faFlag,
|
||||||
faGlasses,
|
faGlasses,
|
||||||
|
faLock,
|
||||||
faPalette,
|
faPalette,
|
||||||
faReply,
|
faReply,
|
||||||
faRotate,
|
faRotate,
|
||||||
|
@ -21,14 +22,18 @@ import {
|
||||||
faShip,
|
faShip,
|
||||||
faSparkles,
|
faSparkles,
|
||||||
faSwords,
|
faSwords,
|
||||||
|
faXmark,
|
||||||
} from "@fortawesome/pro-solid-svg-icons"
|
} from "@fortawesome/pro-solid-svg-icons"
|
||||||
import { useDrawProps } from "@hooks/useDrawProps"
|
import { useDrawProps } from "@hooks/useDrawProps"
|
||||||
import { useGameProps } from "@hooks/useGameProps"
|
import { useGameProps } from "@hooks/useGameProps"
|
||||||
|
import useIndex from "@hooks/useIndex"
|
||||||
import useShips from "@hooks/useShips"
|
import useShips from "@hooks/useShips"
|
||||||
import { socket } from "@lib/socket"
|
import { socket } from "@lib/socket"
|
||||||
|
import { modes } from "@lib/utils/helpers"
|
||||||
import { GamePropsSchema } from "@lib/zodSchemas"
|
import { GamePropsSchema } from "@lib/zodSchemas"
|
||||||
import { useSession } from "next-auth/react"
|
import { useRouter } from "next/router"
|
||||||
import { useCallback, useEffect, useMemo } from "react"
|
import { useCallback, useEffect, useMemo } from "react"
|
||||||
|
import { Icons, toast } from "react-toastify"
|
||||||
|
|
||||||
export function setGameSetting(
|
export function setGameSetting(
|
||||||
payload: GameSettings,
|
payload: GameSettings,
|
||||||
|
@ -47,26 +52,27 @@ export function setGameSetting(
|
||||||
|
|
||||||
function EventBar({ clear }: { clear: () => void }) {
|
function EventBar({ clear }: { clear: () => void }) {
|
||||||
const { shouldHide, color } = useDrawProps()
|
const { shouldHide, color } = useDrawProps()
|
||||||
const { data: session } = useSession()
|
const { selfIndex, isActiveIndex, selfUser } = useIndex()
|
||||||
|
const { ships } = useShips()
|
||||||
|
const router = useRouter()
|
||||||
const {
|
const {
|
||||||
payload,
|
payload,
|
||||||
|
userStates,
|
||||||
menu,
|
menu,
|
||||||
mode,
|
mode,
|
||||||
setSetting,
|
setSetting,
|
||||||
full,
|
full,
|
||||||
|
target,
|
||||||
setTarget,
|
setTarget,
|
||||||
setTargetPreview,
|
setTargetPreview,
|
||||||
setIsReady,
|
setIsReady,
|
||||||
|
reset,
|
||||||
} = useGameProps()
|
} = useGameProps()
|
||||||
const { ships } = useShips()
|
|
||||||
const gameSetting = useCallback(
|
const gameSetting = useCallback(
|
||||||
(payload: GameSettings) => setGameSetting(payload, setSetting, full),
|
(payload: GameSettings) => setGameSetting(payload, setSetting, full),
|
||||||
[full, setSetting]
|
[full, setSetting]
|
||||||
)
|
)
|
||||||
const self = useMemo(
|
|
||||||
() => payload?.users.find((e) => e?.id === session?.user.id),
|
|
||||||
[payload?.users, session?.user.id]
|
|
||||||
)
|
|
||||||
const items = useMemo<EventBarModes>(
|
const items = useMemo<EventBarModes>(
|
||||||
() => ({
|
() => ({
|
||||||
main: [
|
main: [
|
||||||
|
@ -82,14 +88,14 @@ function EventBar({ clear }: { clear: () => void }) {
|
||||||
icon: faSwords,
|
icon: faSwords,
|
||||||
text: "Attack",
|
text: "Attack",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
useGameProps.setState({ menu: "actions" })
|
useGameProps.setState({ menu: "moves" })
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
icon: faShip,
|
icon: faShip,
|
||||||
text: "Ships",
|
text: "Ships",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
useGameProps.setState({ menu: "actions" })
|
useGameProps.setState({ menu: "moves" })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -109,20 +115,21 @@ function EventBar({ clear }: { clear: () => void }) {
|
||||||
],
|
],
|
||||||
menu: [
|
menu: [
|
||||||
{
|
{
|
||||||
icon: faArrowRightFromBracket,
|
icon: faFlag,
|
||||||
text: "Leave",
|
text: "Surrender",
|
||||||
iconColor: "darkred",
|
iconColor: "darkred",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
// router.push()
|
useGameProps.setState({ menu: "surrender" })
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
actions:
|
moves:
|
||||||
payload?.game?.state === "running"
|
payload?.game?.state === "running"
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
icon: "scope",
|
icon: "scope",
|
||||||
text: "Fire missile",
|
text: "Fire missile",
|
||||||
|
enabled: mode === 0,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
useGameProps.setState({ mode: 0 })
|
useGameProps.setState({ mode: 0 })
|
||||||
setTarget((e) => ({ ...e, show: false }))
|
setTarget((e) => ({ ...e, show: false }))
|
||||||
|
@ -131,10 +138,11 @@ function EventBar({ clear }: { clear: () => void }) {
|
||||||
{
|
{
|
||||||
icon: "torpedo",
|
icon: "torpedo",
|
||||||
text: "Fire torpedo",
|
text: "Fire torpedo",
|
||||||
|
enabled: mode === 1 || mode === 2,
|
||||||
amount:
|
amount:
|
||||||
2 -
|
2 -
|
||||||
(self?.moves.filter(
|
((selfUser?.moves ?? []).filter(
|
||||||
(e) => e.action === "htorpedo" || e.action === "vtorpedo"
|
(e) => e.type === "htorpedo" || e.type === "vtorpedo"
|
||||||
).length ?? 0),
|
).length ?? 0),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
useGameProps.setState({ mode: 1 })
|
useGameProps.setState({ mode: 1 })
|
||||||
|
@ -144,9 +152,11 @@ function EventBar({ clear }: { clear: () => void }) {
|
||||||
{
|
{
|
||||||
icon: "radar",
|
icon: "radar",
|
||||||
text: "Radar scan",
|
text: "Radar scan",
|
||||||
|
enabled: mode === 3,
|
||||||
amount:
|
amount:
|
||||||
1 -
|
1 -
|
||||||
(self?.moves.filter((e) => e.action === "radar").length ?? 0),
|
((selfUser?.moves ?? []).filter((e) => e.type === "radar")
|
||||||
|
.length ?? 0),
|
||||||
callback: () => {
|
callback: () => {
|
||||||
useGameProps.setState({ mode: 3 })
|
useGameProps.setState({ mode: 3 })
|
||||||
setTarget((e) => ({ ...e, show: false }))
|
setTarget((e) => ({ ...e, show: false }))
|
||||||
|
@ -191,19 +201,6 @@ function EventBar({ clear }: { clear: () => void }) {
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: faCheck,
|
|
||||||
text: "Done",
|
|
||||||
disabled: mode >= 0,
|
|
||||||
callback: () => {
|
|
||||||
if (!payload || !session?.user.id) return
|
|
||||||
const i = payload.users.findIndex(
|
|
||||||
(user) => session.user.id === user?.id
|
|
||||||
)
|
|
||||||
setIsReady({ isReady: true, i })
|
|
||||||
socket.emit("isReady", true)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
draw: [
|
draw: [
|
||||||
{ icon: faBroomWide, text: "Clear", callback: clear },
|
{ icon: faBroomWide, text: "Clear", callback: clear },
|
||||||
|
@ -248,39 +245,89 @@ function EventBar({ clear }: { clear: () => void }) {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
surrender: [
|
||||||
|
{
|
||||||
|
icon: faCheck,
|
||||||
|
text: "Yes",
|
||||||
|
iconColor: "green",
|
||||||
|
callback: async () => {
|
||||||
|
socket.emit("gameState", "aborted")
|
||||||
|
await router.push("/")
|
||||||
|
reset()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: faXmark,
|
||||||
|
text: "No",
|
||||||
|
iconColor: "red",
|
||||||
|
callback: () => {
|
||||||
|
useGameProps.setState({ menu: "main" })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
|
payload?.game?.state,
|
||||||
|
payload?.game?.allowSpectators,
|
||||||
|
payload?.game?.allowSpecials,
|
||||||
|
payload?.game?.allowChat,
|
||||||
|
payload?.game?.allowMarkDraw,
|
||||||
|
mode,
|
||||||
|
selfUser?.moves,
|
||||||
|
ships,
|
||||||
clear,
|
clear,
|
||||||
color,
|
color,
|
||||||
|
shouldHide,
|
||||||
gameSetting,
|
gameSetting,
|
||||||
mode,
|
|
||||||
payload,
|
|
||||||
self?.moves,
|
|
||||||
session?.user.id,
|
|
||||||
setIsReady,
|
|
||||||
setTarget,
|
setTarget,
|
||||||
setTargetPreview,
|
setTargetPreview,
|
||||||
ships,
|
router,
|
||||||
shouldHide,
|
reset,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
menu !== "actions" ||
|
menu !== "moves" ||
|
||||||
payload?.game?.state !== "starting" ||
|
payload?.game?.state !== "starting" ||
|
||||||
mode < 0 ||
|
mode < 0 ||
|
||||||
items.actions[mode].amount
|
items.moves[mode].amount
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
const index = items.actions.findIndex((e) => e.amount)
|
const index = items.moves.findIndex((e) => e.amount)
|
||||||
useGameProps.setState({ mode: index })
|
useGameProps.setState({ mode: index })
|
||||||
}, [items.actions, menu, mode, payload?.game?.state])
|
}, [items.moves, menu, mode, payload?.game?.state])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
useDrawProps.setState({ enable: menu === "draw" })
|
useDrawProps.setState({ enable: menu === "draw" })
|
||||||
}, [menu])
|
}, [menu])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (payload?.game?.state !== "running") return
|
||||||
|
|
||||||
|
let toastId = "otherPlayer"
|
||||||
|
if (isActiveIndex) toast.dismiss(toastId)
|
||||||
|
else
|
||||||
|
toast.info("Waiting for other player...", {
|
||||||
|
toastId,
|
||||||
|
position: "top-right",
|
||||||
|
icon: Icons.spinner(),
|
||||||
|
autoClose: false,
|
||||||
|
hideProgressBar: true,
|
||||||
|
closeButton: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// toastId = "connect_error"
|
||||||
|
// const isActive = toast.isActive(toastId)
|
||||||
|
// console.log(toastId, isActive)
|
||||||
|
// if (isActive)
|
||||||
|
// toast.update(toastId, {
|
||||||
|
// autoClose: 5000,
|
||||||
|
// })
|
||||||
|
// else
|
||||||
|
// toast.warn("Spie", { toastId })
|
||||||
|
}, [isActiveIndex, menu, payload?.game?.state])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="event-bar">
|
<div className="event-bar">
|
||||||
{menu !== "main" && (
|
{menu !== "main" && (
|
||||||
|
@ -295,9 +342,50 @@ function EventBar({ clear }: { clear: () => void }) {
|
||||||
}}
|
}}
|
||||||
></Item>
|
></Item>
|
||||||
)}
|
)}
|
||||||
{items[menu].map((e, i) => (
|
{items[menu].map((e, i) => {
|
||||||
<Item key={i} props={e} />
|
if (!isActiveIndex && menu === "main" && i === 1) return
|
||||||
))}
|
return <Item key={i} props={e} />
|
||||||
|
})}
|
||||||
|
{menu === "moves" && (
|
||||||
|
<Item
|
||||||
|
props={{
|
||||||
|
icon:
|
||||||
|
selfIndex >= 0 && userStates[selfIndex].isReady
|
||||||
|
? faLock
|
||||||
|
: faCheck,
|
||||||
|
text:
|
||||||
|
selfIndex >= 0 && userStates[selfIndex].isReady
|
||||||
|
? "unready"
|
||||||
|
: "Done",
|
||||||
|
disabled:
|
||||||
|
payload?.game?.state === "starting" ? mode >= 0 : undefined,
|
||||||
|
enabled:
|
||||||
|
payload?.game?.state === "running" && mode >= 0 && target.show,
|
||||||
|
callback: () => {
|
||||||
|
if (selfIndex < 0) return
|
||||||
|
if (payload?.game?.state === "starting") {
|
||||||
|
const isReady = !userStates[selfIndex].isReady
|
||||||
|
setIsReady({ isReady, i: selfIndex })
|
||||||
|
socket.emit("isReady", isReady)
|
||||||
|
}
|
||||||
|
if (payload?.game?.state === "running") {
|
||||||
|
const i = (selfUser?.moves ?? [])
|
||||||
|
.map((e) => e.index)
|
||||||
|
.reduce((prev, curr) => (curr > prev ? curr : prev), 0)
|
||||||
|
const props = {
|
||||||
|
type: modes[mode].type,
|
||||||
|
x: target.x,
|
||||||
|
y: target.y,
|
||||||
|
orientation: target.orientation,
|
||||||
|
index: (selfUser?.moves ?? []).length ? i + 1 : 0,
|
||||||
|
}
|
||||||
|
socket.emit("dispatchMove", props)
|
||||||
|
setTarget((t) => ({ ...t, show: false }))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></Item>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,20 @@ import Targets from "@components/Gamefield/Targets"
|
||||||
import { useDraw } from "@hooks/useDraw"
|
import { useDraw } from "@hooks/useDraw"
|
||||||
import { useDrawProps } from "@hooks/useDrawProps"
|
import { useDrawProps } from "@hooks/useDrawProps"
|
||||||
import { useGameProps } from "@hooks/useGameProps"
|
import { useGameProps } from "@hooks/useGameProps"
|
||||||
|
import useIndex from "@hooks/useIndex"
|
||||||
import useSocket from "@hooks/useSocket"
|
import useSocket from "@hooks/useSocket"
|
||||||
import { socket } from "@lib/socket"
|
import { socket } from "@lib/socket"
|
||||||
import { overlapsWithAnyBorder } from "@lib/utils/helpers"
|
import { overlapsWithAnyBorder } from "@lib/utils/helpers"
|
||||||
|
import { useRouter } from "next/router"
|
||||||
import { CSSProperties } from "react"
|
import { CSSProperties } from "react"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
export const count = 12
|
export const count = 12
|
||||||
|
|
||||||
function Gamefield() {
|
function Gamefield() {
|
||||||
|
const { isActiveIndex, selfUser } = useIndex()
|
||||||
|
const router = useRouter()
|
||||||
const {
|
const {
|
||||||
userStates,
|
userStates,
|
||||||
mode,
|
mode,
|
||||||
|
@ -27,17 +32,22 @@ function Gamefield() {
|
||||||
payload,
|
payload,
|
||||||
setTargetPreview,
|
setTargetPreview,
|
||||||
full,
|
full,
|
||||||
|
reset,
|
||||||
} = useGameProps()
|
} = useGameProps()
|
||||||
const { isConnected } = useSocket()
|
const { isConnected } = useSocket()
|
||||||
|
|
||||||
|
const { canvasRef, onMouseDown, clear } = useDraw()
|
||||||
|
const { enable, color, shouldHide } = useDrawProps()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
payload?.game?.state !== "starting" ||
|
payload?.game?.state !== "starting" ||
|
||||||
userStates.reduce((prev, curr) => prev || !curr.isReady, false)
|
userStates.reduce((prev, curr) => prev || !curr.isReady, false)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
socket.emit("ships", selfUser?.ships ?? [])
|
||||||
socket.emit("gameState", "running")
|
socket.emit("gameState", "running")
|
||||||
}, [payload?.game?.state, userStates])
|
}, [payload?.game?.state, selfUser?.ships, userStates])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (payload?.game?.id || !isConnected) return
|
if (payload?.game?.id || !isConnected) return
|
||||||
|
@ -78,8 +88,18 @@ function Gamefield() {
|
||||||
}
|
}
|
||||||
}, [mode, mouseCursor, payload?.game?.state, setTargetPreview, target])
|
}, [mode, mouseCursor, payload?.game?.state, setTargetPreview, target])
|
||||||
|
|
||||||
const { canvasRef, onMouseDown, clear } = useDraw()
|
useEffect(() => {
|
||||||
const { enable, color, shouldHide } = useDrawProps()
|
if (payload?.game?.state !== "aborted") return
|
||||||
|
toast.info("Enemy gave up!")
|
||||||
|
router.push("/")
|
||||||
|
reset()
|
||||||
|
}, [payload?.game?.state, reset, router])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (payload?.game?.id) return
|
||||||
|
const timeout = setTimeout(() => router.push("/"), 5000)
|
||||||
|
return () => clearTimeout(timeout)
|
||||||
|
}, [payload?.game?.id, router])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="gamefield">
|
<div id="gamefield">
|
||||||
|
@ -98,8 +118,8 @@ function Gamefield() {
|
||||||
<Labeling />
|
<Labeling />
|
||||||
|
|
||||||
{/* Ships */}
|
{/* Ships */}
|
||||||
<Ships />
|
|
||||||
<HitElems />
|
<HitElems />
|
||||||
|
{(payload?.game?.state !== "running" || !isActiveIndex) && <Ships />}
|
||||||
|
|
||||||
{/* Fog images */}
|
{/* Fog images */}
|
||||||
{/* <FogImages /> */}
|
{/* <FogImages /> */}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
import { Target, TargetList } from "../../interfaces/frontend"
|
import { PointerProps } from "../../interfaces/frontend"
|
||||||
import { faCrosshairs } from "@fortawesome/pro-solid-svg-icons"
|
import { faCrosshairs } from "@fortawesome/pro-solid-svg-icons"
|
||||||
import { faRadar } from "@fortawesome/pro-thin-svg-icons"
|
import { faRadar } from "@fortawesome/pro-thin-svg-icons"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import classNames from "classnames"
|
import classNames from "classnames"
|
||||||
import { CSSProperties } from "react"
|
import { CSSProperties } from "react"
|
||||||
|
|
||||||
export interface PointerProps extends Target, TargetList {
|
|
||||||
imply: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
function GamefieldPointer({
|
function GamefieldPointer({
|
||||||
props: { x, y, show, type, edges, imply },
|
props: { x, y, show, type, edges, imply },
|
||||||
preview,
|
preview,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Hit } from "../../interfaces/frontend"
|
import { Hit } from "../../interfaces/frontend"
|
||||||
import { faBurst, faXmark } from "@fortawesome/pro-solid-svg-icons"
|
import { faBurst, faXmark } from "@fortawesome/pro-solid-svg-icons"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import { useGameProps } from "@hooks/useGameProps"
|
import useIndex from "@hooks/useIndex"
|
||||||
import { CSSProperties } from "react"
|
import { CSSProperties } from "react"
|
||||||
|
|
||||||
function HitElems({
|
function HitElems({
|
||||||
|
@ -9,11 +9,11 @@ function HitElems({
|
||||||
}: {
|
}: {
|
||||||
props?: { hits: Hit[]; colorOverride?: string }
|
props?: { hits: Hit[]; colorOverride?: string }
|
||||||
}) {
|
}) {
|
||||||
const { hits } = useGameProps()
|
const { activeUser } = useIndex()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{(props?.hits ?? hits).map(({ hit, x, y }, i) => (
|
{(props?.hits ?? activeUser?.hits ?? []).map(({ hit, x, y }, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="hit-svg"
|
className="hit-svg"
|
||||||
|
|
|
@ -2,11 +2,12 @@ import { ItemProps } from "../../interfaces/frontend"
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
||||||
import { useDrawProps } from "@hooks/useDrawProps"
|
import { useDrawProps } from "@hooks/useDrawProps"
|
||||||
import classNames from "classnames"
|
import classNames from "classnames"
|
||||||
|
import { enable } from "colors"
|
||||||
import React, { CSSProperties, useEffect, useRef, useState } from "react"
|
import React, { CSSProperties, useEffect, useRef, useState } from "react"
|
||||||
import { HexColorPicker } from "react-colorful"
|
import { HexColorPicker } from "react-colorful"
|
||||||
|
|
||||||
function Item({
|
function Item({
|
||||||
props: { icon, text, amount, iconColor, disabled, callback },
|
props: { icon, text, amount, iconColor, disabled, enabled, callback },
|
||||||
}: {
|
}: {
|
||||||
props: ItemProps
|
props: ItemProps
|
||||||
}) {
|
}) {
|
||||||
|
@ -47,7 +48,7 @@ function Item({
|
||||||
className={classNames("container", {
|
className={classNames("container", {
|
||||||
amount: typeof amount !== "undefined",
|
amount: typeof amount !== "undefined",
|
||||||
disabled: disabled || amount === 0,
|
disabled: disabled || amount === 0,
|
||||||
enabled: disabled === false,
|
enabled: disabled === false || enabled,
|
||||||
})}
|
})}
|
||||||
style={
|
style={
|
||||||
typeof amount !== "undefined"
|
typeof amount !== "undefined"
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import Ship from "./Ship"
|
import Ship from "./Ship"
|
||||||
import useShips from "@hooks/useShips"
|
import useIndex from "@hooks/useIndex"
|
||||||
|
|
||||||
function Ships() {
|
function Ships() {
|
||||||
const { ships } = useShips()
|
const { selfUser } = useIndex()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ships.map((props, i) => (
|
{selfUser?.ships.map((props, i) => (
|
||||||
<Ship key={i} props={props} />
|
<Ship key={i} props={props} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import GamefieldPointer from "./GamefieldPointer"
|
||||||
import HitElems from "./HitElems"
|
import HitElems from "./HitElems"
|
||||||
import Ship from "./Ship"
|
import Ship from "./Ship"
|
||||||
import { useGameProps } from "@hooks/useGameProps"
|
import { useGameProps } from "@hooks/useGameProps"
|
||||||
|
import useIndex from "@hooks/useIndex"
|
||||||
import useShips from "@hooks/useShips"
|
import useShips from "@hooks/useShips"
|
||||||
import {
|
import {
|
||||||
composeTargetTiles,
|
composeTargetTiles,
|
||||||
|
@ -10,17 +11,22 @@ import {
|
||||||
} from "@lib/utils/helpers"
|
} from "@lib/utils/helpers"
|
||||||
|
|
||||||
function Targets() {
|
function Targets() {
|
||||||
const { payload, target, targetPreview, mode, hits } = useGameProps()
|
const { activeUser } = useIndex()
|
||||||
|
const { payload, target, targetPreview, mode } = useGameProps()
|
||||||
const { ships } = useShips()
|
const { ships } = useShips()
|
||||||
|
|
||||||
if (payload?.game?.state === "running")
|
if (payload?.game?.state === "running")
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{[
|
{[
|
||||||
...composeTargetTiles(target, mode, hits).map((props, i) => (
|
...composeTargetTiles(target, mode, activeUser?.hits ?? []).map(
|
||||||
<GamefieldPointer key={"t" + i} props={props} />
|
(props, i) => <GamefieldPointer key={"t" + i} props={props} />
|
||||||
)),
|
),
|
||||||
...composeTargetTiles(targetPreview, mode, hits).map((props, i) => (
|
...composeTargetTiles(
|
||||||
|
targetPreview,
|
||||||
|
mode,
|
||||||
|
activeUser?.hits ?? []
|
||||||
|
).map((props, i) => (
|
||||||
<GamefieldPointer key={"p" + i} props={props} preview />
|
<GamefieldPointer key={"p" + i} props={props} preview />
|
||||||
)),
|
)),
|
||||||
]}
|
]}
|
||||||
|
|
|
@ -48,7 +48,7 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!launching || launchTime > 0) return
|
if (!launching || launchTime > 0) return
|
||||||
socket.emit("gameState", "running")
|
socket.emit("gameState", "starting")
|
||||||
}, [launching, launchTime, router])
|
}, [launching, launchTime, router])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -73,7 +73,7 @@ function LobbyFrame({ openSettings }: { openSettings: () => void }) {
|
||||||
payload?.game?.state === "lobby"
|
payload?.game?.state === "lobby"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
router.push("gamefield")
|
router.push("/gamefield")
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -7,12 +7,12 @@ import { ReactNode } from "react"
|
||||||
|
|
||||||
function OptionButton({
|
function OptionButton({
|
||||||
icon,
|
icon,
|
||||||
action,
|
callback,
|
||||||
children,
|
children,
|
||||||
disabled,
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
icon: FontAwesomeIconProps["icon"]
|
icon: FontAwesomeIconProps["icon"]
|
||||||
action?: () => void
|
callback?: () => void
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
@ -24,7 +24,7 @@ function OptionButton({
|
||||||
? "border-b-4 border-shield-gray bg-voidDark active:border-b-0 active:border-t-4"
|
? "border-b-4 border-shield-gray bg-voidDark active:border-b-0 active:border-t-4"
|
||||||
: "border-4 border-dashed border-slate-600 bg-red-950"
|
: "border-4 border-dashed border-slate-600 bg-red-950"
|
||||||
)}
|
)}
|
||||||
onClick={() => action && setTimeout(action, 200)}
|
onClick={() => callback && setTimeout(callback, 200)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
title={!disabled ? "" : "Please login"}
|
title={!disabled ? "" : "Please login"}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
ActionDispatchProps,
|
MoveDispatchProps,
|
||||||
EventBarModes,
|
EventBarModes,
|
||||||
Hit,
|
|
||||||
MouseCursor,
|
MouseCursor,
|
||||||
ShipProps,
|
ShipProps,
|
||||||
Target,
|
Target,
|
||||||
|
@ -14,13 +13,15 @@ import {
|
||||||
initlialMouseCursor,
|
initlialMouseCursor,
|
||||||
initlialTarget,
|
initlialTarget,
|
||||||
initlialTargetPreview,
|
initlialTargetPreview,
|
||||||
|
intersectingShip,
|
||||||
|
targetList,
|
||||||
} from "@lib/utils/helpers"
|
} from "@lib/utils/helpers"
|
||||||
import {
|
import {
|
||||||
GamePropsSchema,
|
GamePropsSchema,
|
||||||
optionalGamePropsSchema,
|
optionalGamePropsSchema,
|
||||||
PlayerSchema,
|
PlayerSchema,
|
||||||
} from "@lib/zodSchemas"
|
} from "@lib/zodSchemas"
|
||||||
import { GameState } from "@prisma/client"
|
import { GameState, MoveType } from "@prisma/client"
|
||||||
import { produce } from "immer"
|
import { produce } from "immer"
|
||||||
import { SetStateAction } from "react"
|
import { SetStateAction } from "react"
|
||||||
import { toast } from "react-toastify"
|
import { toast } from "react-toastify"
|
||||||
|
@ -34,16 +35,14 @@ const initialState: optionalGamePropsSchema & {
|
||||||
}[]
|
}[]
|
||||||
menu: keyof EventBarModes
|
menu: keyof EventBarModes
|
||||||
mode: number
|
mode: number
|
||||||
hits: Hit[]
|
|
||||||
target: Target
|
target: Target
|
||||||
targetPreview: TargetPreview
|
targetPreview: TargetPreview
|
||||||
mouseCursor: MouseCursor
|
mouseCursor: MouseCursor
|
||||||
} = {
|
} = {
|
||||||
menu: "actions",
|
menu: "moves",
|
||||||
mode: 0,
|
mode: 0,
|
||||||
payload: null,
|
payload: null,
|
||||||
hash: null,
|
hash: null,
|
||||||
hits: [],
|
|
||||||
target: initlialTarget,
|
target: initlialTarget,
|
||||||
targetPreview: initlialTargetPreview,
|
targetPreview: initlialTargetPreview,
|
||||||
mouseCursor: initlialMouseCursor,
|
mouseCursor: initlialMouseCursor,
|
||||||
|
@ -56,7 +55,7 @@ const initialState: optionalGamePropsSchema & {
|
||||||
export type State = typeof initialState
|
export type State = typeof initialState
|
||||||
|
|
||||||
export type Action = {
|
export type Action = {
|
||||||
DispatchAction: (props: ActionDispatchProps) => void
|
DispatchMove: (props: MoveDispatchProps, i: number) => void
|
||||||
setTarget: (target: SetStateAction<Target>) => void
|
setTarget: (target: SetStateAction<Target>) => void
|
||||||
setTargetPreview: (targetPreview: SetStateAction<TargetPreview>) => void
|
setTargetPreview: (targetPreview: SetStateAction<TargetPreview>) => void
|
||||||
setMouseCursor: (mouseCursor: SetStateAction<MouseCursor>) => void
|
setMouseCursor: (mouseCursor: SetStateAction<MouseCursor>) => void
|
||||||
|
@ -66,26 +65,54 @@ export type Action = {
|
||||||
leave: (cb: () => void) => void
|
leave: (cb: () => void) => void
|
||||||
setIsReady: (payload: { i: number; isReady: boolean }) => void
|
setIsReady: (payload: { i: number; isReady: boolean }) => void
|
||||||
gameState: (newState: GameState) => void
|
gameState: (newState: GameState) => void
|
||||||
setShips: (ships: ShipProps[], userId: string) => void
|
setShips: (ships: ShipProps[], index: number) => void
|
||||||
removeShip: (props: ShipProps, userId: string) => void
|
removeShip: (props: ShipProps, index: number) => void
|
||||||
setIsConnected: (payload: { i: number; isConnected: boolean }) => void
|
setIsConnected: (payload: { i: number; isConnected: boolean }) => void
|
||||||
reset: () => void
|
reset: () => void
|
||||||
|
setActiveIndex: (i: number, selfIndex: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGameProps = create<State & Action>()(
|
export const useGameProps = create<State & Action>()(
|
||||||
devtools(
|
devtools(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
...initialState,
|
...initialState,
|
||||||
DispatchAction: (action) =>
|
setActiveIndex: (i, selfIndex) =>
|
||||||
set(
|
set(
|
||||||
produce((state: State) => {
|
produce((state: State) => {
|
||||||
// switch (action.type) {
|
if (!state.payload) return
|
||||||
// case "fireMissile":
|
state.payload.activeIndex = i
|
||||||
// case "htorpedo":
|
if (i === selfIndex) {
|
||||||
// case "vtorpedo": {
|
state.menu = "moves"
|
||||||
// state.hits.push(...action.payload)
|
state.mode = 0
|
||||||
// }
|
} else {
|
||||||
// }
|
state.menu = "main"
|
||||||
|
state.mode = -1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
),
|
||||||
|
DispatchMove: (move, i) =>
|
||||||
|
set(
|
||||||
|
produce((state: State) => {
|
||||||
|
if (!state.payload) return
|
||||||
|
const list = targetList(move, move.type)
|
||||||
|
state.payload.users.map((e) => {
|
||||||
|
if (!e) return e
|
||||||
|
if (i === e.index) e.moves.push(move)
|
||||||
|
else if (move.type !== MoveType.radar)
|
||||||
|
e.hits.push(
|
||||||
|
...list.map(({ x, y }) => ({
|
||||||
|
hit: !!intersectingShip(e.ships, {
|
||||||
|
...move,
|
||||||
|
size: 1,
|
||||||
|
variant: 0,
|
||||||
|
}).fields.length,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
return e
|
||||||
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
setTarget: (dispatch) =>
|
setTarget: (dispatch) =>
|
||||||
|
@ -112,22 +139,22 @@ export const useGameProps = create<State & Action>()(
|
||||||
else state.mouseCursor = dispatch
|
else state.mouseCursor = dispatch
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
setShips: (ships, userId) =>
|
setShips: (ships, index) =>
|
||||||
set(
|
set(
|
||||||
produce((state: State) => {
|
produce((state: State) => {
|
||||||
if (!state.payload) return
|
if (!state.payload) return
|
||||||
state.payload.users = state.payload.users.map((e) => {
|
state.payload.users = state.payload.users.map((e) => {
|
||||||
if (!e || e.id !== userId) return e
|
if (!e || e.index !== index) return e
|
||||||
e.ships = ships
|
e.ships = ships
|
||||||
return e
|
return e
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
removeShip: ({ size, variant, x, y }, userId) =>
|
removeShip: ({ size, variant, x, y }, index) =>
|
||||||
set(
|
set(
|
||||||
produce((state: State) => {
|
produce((state: State) => {
|
||||||
state.payload?.users.map((e) => {
|
state.payload?.users.map((e) => {
|
||||||
if (!e || e.id !== userId) return
|
if (!e || e.index !== index) return
|
||||||
const indexToRemove = e.ships.findIndex(
|
const indexToRemove = e.ships.findIndex(
|
||||||
(ship) =>
|
(ship) =>
|
||||||
ship.size === size &&
|
ship.size === size &&
|
||||||
|
|
24
leaky-ships/hooks/useIndex.ts
Normal file
24
leaky-ships/hooks/useIndex.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { useGameProps } from "./useGameProps"
|
||||||
|
import { useSession } from "next-auth/react"
|
||||||
|
|
||||||
|
function useIndex() {
|
||||||
|
const { payload } = useGameProps()
|
||||||
|
const { data: session } = useSession()
|
||||||
|
|
||||||
|
const selfIndex =
|
||||||
|
payload?.users.findIndex((e) => e?.id === session?.user.id) ?? -1
|
||||||
|
const activeIndex = payload?.activeIndex ?? -1
|
||||||
|
const isActiveIndex = selfIndex >= 0 && payload?.activeIndex === selfIndex
|
||||||
|
const selfUser = payload?.users[selfIndex]
|
||||||
|
const activeUser = payload?.users[activeIndex === 0 ? 1 : 0]
|
||||||
|
|
||||||
|
return {
|
||||||
|
selfIndex,
|
||||||
|
activeIndex,
|
||||||
|
isActiveIndex,
|
||||||
|
selfUser,
|
||||||
|
activeUser,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useIndex
|
27
leaky-ships/hooks/useShips.ts
Normal file
27
leaky-ships/hooks/useShips.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { ShipProps } from "../interfaces/frontend"
|
||||||
|
import { useGameProps } from "./useGameProps"
|
||||||
|
import useIndex from "./useIndex"
|
||||||
|
import { useCallback, useMemo } from "react"
|
||||||
|
|
||||||
|
function useShips() {
|
||||||
|
const gameProps = useGameProps()
|
||||||
|
const { selfIndex } = useIndex()
|
||||||
|
|
||||||
|
const ships = useMemo(
|
||||||
|
() =>
|
||||||
|
gameProps.payload?.users.find((e) => e?.index === selfIndex)?.ships ?? [],
|
||||||
|
[gameProps.payload?.users, selfIndex]
|
||||||
|
)
|
||||||
|
const setShips = useCallback(
|
||||||
|
(ships: ShipProps[]) => gameProps.setShips(ships, selfIndex),
|
||||||
|
[gameProps, selfIndex]
|
||||||
|
)
|
||||||
|
const removeShip = useCallback(
|
||||||
|
(ship: ShipProps) => gameProps.removeShip(ship, selfIndex),
|
||||||
|
[gameProps, selfIndex]
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ships, setShips, removeShip }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useShips
|
|
@ -1,28 +0,0 @@
|
||||||
import { ShipProps } from "../interfaces/frontend"
|
|
||||||
import { useGameProps } from "./useGameProps"
|
|
||||||
import { useSession } from "next-auth/react"
|
|
||||||
import { useCallback, useMemo } from "react"
|
|
||||||
|
|
||||||
function useShips() {
|
|
||||||
const gameProps = useGameProps()
|
|
||||||
const { data: session } = useSession()
|
|
||||||
|
|
||||||
const ships = useMemo(
|
|
||||||
() =>
|
|
||||||
gameProps.payload?.users.find((e) => e?.id === session?.user.id)?.ships ??
|
|
||||||
[],
|
|
||||||
[gameProps.payload?.users, session?.user.id]
|
|
||||||
)
|
|
||||||
const setShips = useCallback(
|
|
||||||
(ships: ShipProps[]) => gameProps.setShips(ships, session?.user.id ?? ""),
|
|
||||||
[gameProps, session?.user.id]
|
|
||||||
)
|
|
||||||
const removeShip = useCallback(
|
|
||||||
(ship: ShipProps) => gameProps.removeShip(ship, session?.user.id ?? ""),
|
|
||||||
[gameProps, session?.user.id]
|
|
||||||
)
|
|
||||||
|
|
||||||
return { ships, setShips, removeShip }
|
|
||||||
}
|
|
||||||
|
|
||||||
export default useShips
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { isAuthenticated } from "../pages/start"
|
import { isAuthenticated } from "../pages/start"
|
||||||
import { useGameProps } from "./useGameProps"
|
import { useGameProps } from "./useGameProps"
|
||||||
|
import useIndex from "./useIndex"
|
||||||
import { socket } from "@lib/socket"
|
import { socket } from "@lib/socket"
|
||||||
import { GamePropsSchema } from "@lib/zodSchemas"
|
import { GamePropsSchema } from "@lib/zodSchemas"
|
||||||
import status from "http-status"
|
import status from "http-status"
|
||||||
import { useSession } from "next-auth/react"
|
|
||||||
import { useRouter } from "next/router"
|
import { useRouter } from "next/router"
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { toast } from "react-toastify"
|
import { toast } from "react-toastify"
|
||||||
|
@ -11,6 +11,7 @@ import { toast } from "react-toastify"
|
||||||
/** This function should only be called once per page, otherwise there will be multiple socket connections and duplicate event listeners. */
|
/** This function should only be called once per page, otherwise there will be multiple socket connections and duplicate event listeners. */
|
||||||
function useSocket() {
|
function useSocket() {
|
||||||
const [isConnectedState, setIsConnectedState] = useState(false)
|
const [isConnectedState, setIsConnectedState] = useState(false)
|
||||||
|
const { selfIndex } = useIndex()
|
||||||
const {
|
const {
|
||||||
payload,
|
payload,
|
||||||
userStates,
|
userStates,
|
||||||
|
@ -20,28 +21,25 @@ function useSocket() {
|
||||||
setIsReady,
|
setIsReady,
|
||||||
gameState,
|
gameState,
|
||||||
setIsConnected,
|
setIsConnected,
|
||||||
|
setActiveIndex,
|
||||||
|
DispatchMove,
|
||||||
|
setShips,
|
||||||
} = useGameProps()
|
} = useGameProps()
|
||||||
const { data: session } = useSession()
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const { i, isIndex } = useMemo(() => {
|
|
||||||
const i = payload?.users.findIndex((user) => session?.user?.id === user?.id)
|
|
||||||
const isIndex = !(i === undefined || i < 0)
|
|
||||||
if (!isIndex) return { i: undefined, isIndex }
|
|
||||||
return { i, isIndex }
|
|
||||||
}, [payload?.users, session?.user?.id])
|
|
||||||
const isConnected = useMemo(
|
const isConnected = useMemo(
|
||||||
() => (isIndex ? userStates[i].isConnected : isConnectedState),
|
() =>
|
||||||
[i, isConnectedState, isIndex, userStates]
|
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
|
||||||
|
[selfIndex, isConnectedState, userStates]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isIndex) return
|
if (selfIndex < 0) return
|
||||||
setIsConnected({
|
setIsConnected({
|
||||||
i,
|
i: selfIndex,
|
||||||
isConnected: isConnectedState,
|
isConnected: isConnectedState,
|
||||||
})
|
})
|
||||||
}, [i, isConnectedState, isIndex, setIsConnected])
|
}, [selfIndex, isConnectedState, setIsConnected])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.on("connect", () => {
|
socket.on("connect", () => {
|
||||||
|
@ -97,7 +95,7 @@ function useSocket() {
|
||||||
i,
|
i,
|
||||||
isConnected: true,
|
isConnected: true,
|
||||||
})
|
})
|
||||||
socket.emit("isReady", userStates[i].isReady)
|
socket.emit("isReady", userStates[selfIndex].isReady)
|
||||||
message = "Player has joined the lobby."
|
message = "Player has joined the lobby."
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -122,6 +120,12 @@ function useSocket() {
|
||||||
|
|
||||||
socket.on("gameState", gameState)
|
socket.on("gameState", gameState)
|
||||||
|
|
||||||
|
socket.on("dispatchMove", DispatchMove)
|
||||||
|
|
||||||
|
socket.on("activeIndex", (i) => setActiveIndex(i, selfIndex))
|
||||||
|
|
||||||
|
socket.on("ships", setShips)
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
console.log("disconnect")
|
console.log("disconnect")
|
||||||
setIsConnectedState(false)
|
setIsConnectedState(false)
|
||||||
|
@ -131,13 +135,17 @@ function useSocket() {
|
||||||
socket.removeAllListeners()
|
socket.removeAllListeners()
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
DispatchMove,
|
||||||
full,
|
full,
|
||||||
gameState,
|
gameState,
|
||||||
router,
|
router,
|
||||||
|
selfIndex,
|
||||||
|
setActiveIndex,
|
||||||
setIsConnected,
|
setIsConnected,
|
||||||
setIsReady,
|
setIsReady,
|
||||||
setPlayer,
|
setPlayer,
|
||||||
setSetting,
|
setSetting,
|
||||||
|
setShips,
|
||||||
userStates,
|
userStates,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -150,7 +158,7 @@ function useSocket() {
|
||||||
.then(isAuthenticated)
|
.then(isAuthenticated)
|
||||||
.then((game) => GamePropsSchema.parse(game))
|
.then((game) => GamePropsSchema.parse(game))
|
||||||
.then((res) => full(res))
|
.then((res) => full(res))
|
||||||
.catch()
|
.catch((e) => console.log(e))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isConnected) return
|
if (isConnected) return
|
||||||
|
@ -162,7 +170,10 @@ function useSocket() {
|
||||||
})
|
})
|
||||||
}, [full, isConnected, payload?.game?.id])
|
}, [full, isConnected, payload?.game?.id])
|
||||||
|
|
||||||
return { isConnected: isIndex ? userStates[i].isConnected : isConnectedState }
|
return {
|
||||||
|
isConnected:
|
||||||
|
selfIndex >= 0 ? userStates[selfIndex].isConnected : isConnectedState,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useSocket
|
export default useSocket
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { DrawLineProps, ShipProps } from "./frontend"
|
import { MoveDispatchProps, DrawLineProps, ShipProps } from "./frontend"
|
||||||
import { GameSettings } from "@components/Lobby/SettingsFrame/Setting"
|
import { GameSettings } from "@components/Lobby/SettingsFrame/Setting"
|
||||||
import { GamePropsSchema, PlayerSchema } from "@lib/zodSchemas"
|
import { GamePropsSchema, PlayerSchema } from "@lib/zodSchemas"
|
||||||
import { GameState, Ship } from "@prisma/client"
|
import { GameState } from "@prisma/client"
|
||||||
import type { Server as HTTPServer } from "http"
|
import type { Server as HTTPServer } from "http"
|
||||||
import type { Socket as NetSocket } from "net"
|
import type { Socket as NetSocket } from "net"
|
||||||
import type { NextApiResponse } from "next"
|
import type { NextApiResponse } from "next"
|
||||||
|
@ -50,7 +50,9 @@ export interface ServerToClientEvents {
|
||||||
"draw-line": (props: DrawLineProps, userIndex: number) => void
|
"draw-line": (props: DrawLineProps, userIndex: number) => void
|
||||||
"canvas-clear": () => void
|
"canvas-clear": () => void
|
||||||
gameState: (newState: GameState) => void
|
gameState: (newState: GameState) => void
|
||||||
ships: (ships: ShipProps[], userId: string) => void
|
ships: (ships: ShipProps[], index: number) => void
|
||||||
|
activeIndex: (index: number) => void
|
||||||
|
dispatchMove: (props: MoveDispatchProps, i: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClientToServerEvents {
|
export interface ClientToServerEvents {
|
||||||
|
@ -66,6 +68,7 @@ export interface ClientToServerEvents {
|
||||||
"canvas-clear": () => void
|
"canvas-clear": () => void
|
||||||
gameState: (newState: GameState) => void
|
gameState: (newState: GameState) => void
|
||||||
ships: (ships: ShipProps[]) => void
|
ships: (ships: ShipProps[]) => void
|
||||||
|
dispatchMove: (props: MoveDispatchProps) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InterServerEvents {
|
interface InterServerEvents {
|
||||||
|
|
|
@ -7,10 +7,13 @@ export interface Position {
|
||||||
}
|
}
|
||||||
export interface Target extends Position {
|
export interface Target extends Position {
|
||||||
show: boolean
|
show: boolean
|
||||||
}
|
|
||||||
export interface TargetPreview extends Target {
|
|
||||||
orientation: Orientation
|
orientation: Orientation
|
||||||
}
|
}
|
||||||
|
export interface TargetPreview extends Target {}
|
||||||
|
export interface PointerProps extends TargetList {
|
||||||
|
show: boolean
|
||||||
|
imply: boolean
|
||||||
|
}
|
||||||
export interface MouseCursor extends Position {
|
export interface MouseCursor extends Position {
|
||||||
shouldShow: boolean
|
shouldShow: boolean
|
||||||
}
|
}
|
||||||
|
@ -28,14 +31,16 @@ export interface ItemProps {
|
||||||
amount?: number
|
amount?: number
|
||||||
iconColor?: string
|
iconColor?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
enabled?: boolean
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
}
|
}
|
||||||
export interface EventBarModes {
|
export interface EventBarModes {
|
||||||
main: ItemProps[]
|
main: ItemProps[]
|
||||||
menu: ItemProps[]
|
menu: ItemProps[]
|
||||||
actions: ItemProps[]
|
moves: ItemProps[]
|
||||||
draw: ItemProps[]
|
draw: ItemProps[]
|
||||||
settings: ItemProps[]
|
settings: ItemProps[]
|
||||||
|
surrender: ItemProps[]
|
||||||
}
|
}
|
||||||
export interface Field extends Position {
|
export interface Field extends Position {
|
||||||
field: string
|
field: string
|
||||||
|
@ -60,7 +65,8 @@ export interface ShipProps extends Position {
|
||||||
export interface IndexedPosition extends Position {
|
export interface IndexedPosition extends Position {
|
||||||
i?: number
|
i?: number
|
||||||
}
|
}
|
||||||
export interface ActionDispatchProps extends Position {
|
export interface MoveDispatchProps extends Position {
|
||||||
index?: number
|
index: number
|
||||||
action: MoveType
|
type: MoveType
|
||||||
|
orientation: Orientation
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@ import type {
|
||||||
Hit,
|
Hit,
|
||||||
IndexedPosition,
|
IndexedPosition,
|
||||||
Mode,
|
Mode,
|
||||||
|
PointerProps,
|
||||||
Position,
|
Position,
|
||||||
ShipProps,
|
ShipProps,
|
||||||
Target,
|
Target,
|
||||||
TargetList,
|
TargetList,
|
||||||
TargetPreview,
|
|
||||||
} from "../../interfaces/frontend"
|
} from "../../interfaces/frontend"
|
||||||
import { count } from "@components/Gamefield/Gamefield"
|
import { count } from "@components/Gamefield/Gamefield"
|
||||||
import { PointerProps } from "@components/Gamefield/GamefieldPointer"
|
|
||||||
import { Orientation } from "@prisma/client"
|
import { Orientation } from "@prisma/client"
|
||||||
|
|
||||||
export function borderCN(count: number, x: number, y: number) {
|
export function borderCN(count: number, x: number, y: number) {
|
||||||
|
@ -30,7 +29,7 @@ export function fieldIndex(count: number, x: number, y: number) {
|
||||||
return y * (count + 2) + x
|
return y * (count + 2) + x
|
||||||
}
|
}
|
||||||
|
|
||||||
const modes: Mode[] = [
|
export const modes: Mode[] = [
|
||||||
{
|
{
|
||||||
pointerGrid: Array.from(Array(1), () => Array.from(Array(1))),
|
pointerGrid: Array.from(Array(1), () => Array.from(Array(1))),
|
||||||
type: "missile",
|
type: "missile",
|
||||||
|
@ -59,8 +58,12 @@ export function isAlreadyHit(x: number, y: number, hits: Hit[]) {
|
||||||
|
|
||||||
export function targetList(
|
export function targetList(
|
||||||
{ x: targetX, y: targetY }: Position,
|
{ x: targetX, y: targetY }: Position,
|
||||||
mode: number
|
modeInput: number | string
|
||||||
): TargetList[] {
|
): TargetList[] {
|
||||||
|
const mode =
|
||||||
|
typeof modeInput === "number"
|
||||||
|
? modeInput
|
||||||
|
: modes.findIndex((e) => e.type === modeInput)
|
||||||
if (mode < 0) return []
|
if (mode < 0) return []
|
||||||
const { pointerGrid, type } = modes[mode]
|
const { pointerGrid, type } = modes[mode]
|
||||||
const xLength = pointerGrid.length
|
const xLength = pointerGrid.length
|
||||||
|
@ -112,6 +115,7 @@ export const initlialTarget = {
|
||||||
x: 2,
|
x: 2,
|
||||||
y: 2,
|
y: 2,
|
||||||
show: false,
|
show: false,
|
||||||
|
orientation: Orientation.h,
|
||||||
}
|
}
|
||||||
export const initlialTargetPreview = {
|
export const initlialTargetPreview = {
|
||||||
x: 2,
|
x: 2,
|
||||||
|
|
|
@ -16,11 +16,11 @@ export const PlayerSchema = z
|
||||||
.array(),
|
.array(),
|
||||||
moves: z
|
moves: z
|
||||||
.object({
|
.object({
|
||||||
id: z.string(),
|
|
||||||
index: z.number(),
|
index: z.number(),
|
||||||
action: z.nativeEnum(MoveType),
|
type: z.nativeEnum(MoveType),
|
||||||
x: z.number(),
|
x: z.number(),
|
||||||
y: z.number(),
|
y: z.number(),
|
||||||
|
orientation: z.nativeEnum(Orientation),
|
||||||
})
|
})
|
||||||
.array(),
|
.array(),
|
||||||
ships: z
|
ships: z
|
||||||
|
@ -32,6 +32,13 @@ export const PlayerSchema = z
|
||||||
orientation: z.nativeEnum(Orientation),
|
orientation: z.nativeEnum(Orientation),
|
||||||
})
|
})
|
||||||
.array(),
|
.array(),
|
||||||
|
hits: z
|
||||||
|
.object({
|
||||||
|
x: z.number(),
|
||||||
|
y: z.number(),
|
||||||
|
hit: z.boolean(),
|
||||||
|
})
|
||||||
|
.array(),
|
||||||
})
|
})
|
||||||
.nullable()
|
.nullable()
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ export default async function create(
|
||||||
users: {
|
users: {
|
||||||
create: {
|
create: {
|
||||||
userId: id,
|
userId: id,
|
||||||
index: 1,
|
index: 0,
|
||||||
chats: {
|
chats: {
|
||||||
create: {
|
create: {
|
||||||
event: "created",
|
event: "created",
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default async function join(
|
||||||
data: {
|
data: {
|
||||||
gameId: game.id,
|
gameId: game.id,
|
||||||
userId: id,
|
userId: id,
|
||||||
index: 2,
|
index: 1,
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
game: gameSelects,
|
game: gameSelects,
|
||||||
|
|
|
@ -34,9 +34,8 @@ export const gameSelects = {
|
||||||
},
|
},
|
||||||
moves: {
|
moves: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
|
||||||
index: true,
|
index: true,
|
||||||
action: true,
|
type: true,
|
||||||
x: true,
|
x: true,
|
||||||
y: true,
|
y: true,
|
||||||
orientation: true,
|
orientation: true,
|
||||||
|
@ -51,6 +50,13 @@ export const gameSelects = {
|
||||||
orientation: true,
|
orientation: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
hits: {
|
||||||
|
select: {
|
||||||
|
x: true,
|
||||||
|
y: true,
|
||||||
|
hit: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
user: {
|
user: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
@ -102,10 +108,17 @@ export function composeBody(
|
||||||
...user,
|
...user,
|
||||||
}))
|
}))
|
||||||
.sort((user1, user2) => user1.index - user2.index)
|
.sort((user1, user2) => user1.index - user2.index)
|
||||||
|
let activeIndex = undefined
|
||||||
|
if (game.state === "running") {
|
||||||
|
const l1 = game.users[0].moves.length
|
||||||
|
const l2 = game.users[1].moves.length
|
||||||
|
activeIndex = l1 > l2 ? 1 : 0
|
||||||
|
}
|
||||||
const payload = {
|
const payload = {
|
||||||
game: game,
|
game: game,
|
||||||
gamePin: gamePin?.pin ?? null,
|
gamePin: gamePin?.pin ?? null,
|
||||||
users,
|
users,
|
||||||
|
activeIndex,
|
||||||
}
|
}
|
||||||
return getPayloadwithChecksum(payload)
|
return getPayloadwithChecksum(payload)
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,10 +214,17 @@ const SocketHandler = async (
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
io.to(socket.data.gameId).emit("gameState", newState)
|
io.to(socket.data.gameId).emit("gameState", newState)
|
||||||
|
if (newState === "running")
|
||||||
|
io.to(socket.data.gameId).emit("activeIndex", 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("ships", async (ships) => {
|
socket.on("ships", async (ships) => {
|
||||||
if (!socket.data.gameId || !socket.data.user?.id) return
|
if (
|
||||||
|
!socket.data.gameId ||
|
||||||
|
!socket.data.user?.id ||
|
||||||
|
typeof socket.data.index === "undefined"
|
||||||
|
)
|
||||||
|
return
|
||||||
await prisma.user_Game.update({
|
await prisma.user_Game.update({
|
||||||
where: {
|
where: {
|
||||||
gameId_userId: {
|
gameId_userId: {
|
||||||
|
@ -234,7 +241,38 @@ const SocketHandler = async (
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
socket.to(socket.data.gameId).emit("ships", ships, socket.data.user.id)
|
socket.to(socket.data.gameId).emit("ships", ships, socket.data.index)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on("dispatchMove", async (props) => {
|
||||||
|
if (
|
||||||
|
!socket.data.gameId ||
|
||||||
|
!socket.data.user?.id ||
|
||||||
|
typeof socket.data.index === "undefined"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
const user_Game = await prisma.user_Game
|
||||||
|
.update({
|
||||||
|
where: {
|
||||||
|
gameId_userId: {
|
||||||
|
gameId: socket.data.gameId,
|
||||||
|
userId: socket.data.user?.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
moves: {
|
||||||
|
create: props,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: { game: gameSelects },
|
||||||
|
})
|
||||||
|
.catch((e) => console.log(e, props))
|
||||||
|
if (!user_Game?.game) return
|
||||||
|
const game = user_Game.game
|
||||||
|
const l1 = game.users[0].moves.length
|
||||||
|
const l2 = game.users[1].moves.length
|
||||||
|
io.to(socket.data.gameId).emit("dispatchMove", props, socket.data.index)
|
||||||
|
io.to(socket.data.gameId).emit("activeIndex", l1 > l2 ? 1 : 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("disconnecting", async () => {
|
socket.on("disconnecting", async () => {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import BurgerMenu from "@components/BurgerMenu"
|
import BurgerMenu from "@components/BurgerMenu"
|
||||||
import Logo from "@components/Logo"
|
import Logo from "@components/Logo"
|
||||||
import { faCirclePlay } from "@fortawesome/pro-thin-svg-icons"
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
|
|
||||||
import { useRouter } from "next/router"
|
import { useRouter } from "next/router"
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
@ -12,11 +10,11 @@ export default function Home() {
|
||||||
<div className="mx-auto flex h-full max-w-screen-md flex-col items-center justify-evenly">
|
<div className="mx-auto flex h-full max-w-screen-md flex-col items-center justify-evenly">
|
||||||
<Logo />
|
<Logo />
|
||||||
<BurgerMenu />
|
<BurgerMenu />
|
||||||
<div className="flex h-36 w-64 items-center justify-center rounded-xl border-4 border-black bg-[#2227] sm:h-48 sm:w-96 md:h-72 md:w-[32rem] md:border-[6px] xl:h-[26rem] xl:w-[48rem]">
|
<div className="flex h-36 w-64 items-center justify-center overflow-hidden rounded-xl border-8 border-black bg-[#2227] sm:h-48 sm:w-96 md:h-72 md:w-[32rem] md:border-[6px] xl:h-[26rem] xl:w-[48rem]">
|
||||||
<FontAwesomeIcon
|
<video controls>
|
||||||
className="text-6xl sm:text-7xl md:text-8xl"
|
<source src="/Regelwerk.mp4" type="video/mp4" />
|
||||||
icon={faCirclePlay}
|
Your browser does not support the video tag.
|
||||||
/>
|
</video>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="font-farro rounded-lg border-b-4 border-orange-400 bg-warn px-12 pb-4 pt-5 text-2xl font-bold duration-100 active:border-b-0 active:border-t-4 sm:rounded-xl sm:border-b-[6px] sm:px-14 sm:pb-5 sm:pt-6 sm:text-3xl sm:active:border-t-[6px] md:rounded-2xl md:border-b-8 md:px-20 md:pb-6 md:pt-7 md:text-4xl md:active:border-t-8 xl:px-24 xl:pb-8 xl:pt-10 xl:text-5xl"
|
className="font-farro rounded-lg border-b-4 border-orange-400 bg-warn px-12 pb-4 pt-5 text-2xl font-bold duration-100 active:border-b-0 active:border-t-4 sm:rounded-xl sm:border-b-[6px] sm:px-14 sm:pb-5 sm:pt-6 sm:text-3xl sm:active:border-t-[6px] md:rounded-2xl md:border-b-8 md:px-20 md:pb-6 md:pt-7 md:text-4xl md:active:border-t-8 xl:px-24 xl:pb-8 xl:pt-10 xl:text-5xl"
|
||||||
|
|
|
@ -73,9 +73,9 @@ export default function Start() {
|
||||||
.then(isAuthenticated)
|
.then(isAuthenticated)
|
||||||
.then((game) => GamePropsSchema.parse(game))
|
.then((game) => GamePropsSchema.parse(game))
|
||||||
|
|
||||||
const action = !pin ? "erstellt" : "angefragt"
|
const move = !pin ? "erstellt" : "angefragt"
|
||||||
const toastId = "pageLoad"
|
const toastId = "pageLoad"
|
||||||
toast("Raum wird " + action, {
|
toast("Raum wird " + move, {
|
||||||
icon: Icons.spinner(),
|
icon: Icons.spinner(),
|
||||||
toastId,
|
toastId,
|
||||||
autoClose: false,
|
autoClose: false,
|
||||||
|
@ -150,14 +150,14 @@ export default function Start() {
|
||||||
</button>
|
</button>
|
||||||
<div className="flex flex-col items-center gap-6 sm:gap-12">
|
<div className="flex flex-col items-center gap-6 sm:gap-12">
|
||||||
<OptionButton
|
<OptionButton
|
||||||
action={() => gameFetch()}
|
callback={() => gameFetch()}
|
||||||
icon={faPlus}
|
icon={faPlus}
|
||||||
disabled={!session}
|
disabled={!session}
|
||||||
>
|
>
|
||||||
Raum erstellen
|
Raum erstellen
|
||||||
</OptionButton>
|
</OptionButton>
|
||||||
<OptionButton
|
<OptionButton
|
||||||
action={() => {
|
callback={() => {
|
||||||
router.push({
|
router.push({
|
||||||
pathname: router.pathname,
|
pathname: router.pathname,
|
||||||
query: { q: "join" },
|
query: { q: "join" },
|
||||||
|
@ -185,7 +185,7 @@ export default function Start() {
|
||||||
</OptionButton>
|
</OptionButton>
|
||||||
<OptionButton
|
<OptionButton
|
||||||
icon={faEye}
|
icon={faEye}
|
||||||
action={() => {
|
callback={() => {
|
||||||
router.push({
|
router.push({
|
||||||
pathname: router.pathname,
|
pathname: router.pathname,
|
||||||
query: { q: "watch" },
|
query: { q: "watch" },
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -68,27 +68,12 @@ model VerificationToken {
|
||||||
@@map("verificationtokens")
|
@@map("verificationtokens")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Orientation {
|
|
||||||
h
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
model Ship {
|
|
||||||
id String @id @default(cuid())
|
|
||||||
size Int
|
|
||||||
variant Int
|
|
||||||
x Int
|
|
||||||
y Int
|
|
||||||
orientation Orientation
|
|
||||||
user_GameId String
|
|
||||||
User_Game User_Game @relation(fields: [user_GameId], references: [id], onDelete: Cascade)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum GameState {
|
enum GameState {
|
||||||
lobby
|
lobby
|
||||||
starting
|
starting
|
||||||
running
|
running
|
||||||
ended
|
ended
|
||||||
|
aborted
|
||||||
}
|
}
|
||||||
|
|
||||||
model Game {
|
model Game {
|
||||||
|
@ -112,6 +97,31 @@ model Gamepin {
|
||||||
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
|
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Orientation {
|
||||||
|
h
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
model Ship {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
size Int
|
||||||
|
variant Int
|
||||||
|
x Int
|
||||||
|
y Int
|
||||||
|
orientation Orientation
|
||||||
|
user_GameId String
|
||||||
|
User_Game User_Game @relation(fields: [user_GameId], references: [id], onDelete: Cascade)
|
||||||
|
}
|
||||||
|
|
||||||
|
model Hit {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
x Int
|
||||||
|
y Int
|
||||||
|
hit Boolean
|
||||||
|
user_GameId String
|
||||||
|
User_Game User_Game @relation(fields: [user_GameId], references: [id], onDelete: Cascade)
|
||||||
|
}
|
||||||
|
|
||||||
model User_Game {
|
model User_Game {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
@ -120,6 +130,7 @@ model User_Game {
|
||||||
index Int
|
index Int
|
||||||
moves Move[]
|
moves Move[]
|
||||||
ships Ship[]
|
ships Ship[]
|
||||||
|
hits Hit[]
|
||||||
chats Chat[]
|
chats Chat[]
|
||||||
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
|
game Game @relation(fields: [gameId], references: [id], onDelete: Cascade)
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
@ -129,25 +140,22 @@ model User_Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MoveType {
|
enum MoveType {
|
||||||
radar
|
|
||||||
htorpedo
|
|
||||||
vtorpedo
|
|
||||||
missile
|
missile
|
||||||
|
vtorpedo
|
||||||
|
htorpedo
|
||||||
|
radar
|
||||||
}
|
}
|
||||||
|
|
||||||
model Move {
|
model Move {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
index Int
|
index Int
|
||||||
action MoveType
|
type MoveType
|
||||||
x Int
|
x Int
|
||||||
y Int
|
y Int
|
||||||
orientation Orientation
|
orientation Orientation
|
||||||
user_game_id String
|
user_game_id String
|
||||||
user_game User_Game @relation(fields: [user_game_id], references: [id], onDelete: Cascade)
|
user_game User_Game @relation(fields: [user_game_id], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@unique([user_game_id, index])
|
|
||||||
@@unique([action, x, y])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Chat {
|
model Chat {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue