Working player wise drawing
This commit is contained in:
parent
d245009c37
commit
c8a5c47b98
7 changed files with 137 additions and 77 deletions
|
@ -26,6 +26,7 @@ import { modes } from "~/lib/utils/helpers"
|
||||||
// import { Icons, toast } from "react-toastify"
|
// import { Icons, toast } from "react-toastify"
|
||||||
import { For, Show, createEffect } from "solid-js"
|
import { For, Show, createEffect } from "solid-js"
|
||||||
import { useNavigate } from "solid-start"
|
import { useNavigate } from "solid-start"
|
||||||
|
import { clearDrawing } from "~/hooks/useDraw"
|
||||||
import {
|
import {
|
||||||
color,
|
color,
|
||||||
setEnable,
|
setEnable,
|
||||||
|
@ -47,7 +48,7 @@ import { useSession } from "~/hooks/useSession"
|
||||||
import { EventBarModes } from "../../interfaces/frontend"
|
import { EventBarModes } from "../../interfaces/frontend"
|
||||||
import Item from "./Item"
|
import Item from "./Item"
|
||||||
|
|
||||||
function EventBar(props: { clear: () => void }) {
|
function EventBar() {
|
||||||
const { selfIndex, selfIsActiveIndex, selfUser, ships } = useSession()
|
const { selfIndex, selfIsActiveIndex, selfUser, ships } = useSession()
|
||||||
const navigator = useNavigate()
|
const navigator = useNavigate()
|
||||||
|
|
||||||
|
@ -60,21 +61,24 @@ function EventBar(props: { clear: () => void }) {
|
||||||
setGameProps("menu", "menu")
|
setGameProps("menu", "menu")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
gameProps.gameState === "running"
|
{
|
||||||
? {
|
icon: faSwords,
|
||||||
icon: faSwords,
|
text: "Attack",
|
||||||
text: "Attack",
|
showWhen: () =>
|
||||||
callback: () => {
|
gameProps.gameState === "running" &&
|
||||||
setGameProps("menu", "moves")
|
(selfIsActiveIndex() || gameProps.menu !== "main"),
|
||||||
},
|
callback: () => {
|
||||||
}
|
setGameProps("menu", "moves")
|
||||||
: {
|
},
|
||||||
icon: faShip,
|
},
|
||||||
text: "Ships",
|
{
|
||||||
callback: () => {
|
icon: faShip,
|
||||||
setGameProps("menu", "moves")
|
text: "Ships",
|
||||||
},
|
showWhen: () => gameProps.gameState !== "running",
|
||||||
},
|
callback: () => {
|
||||||
|
setGameProps("menu", "moves")
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: "pen",
|
icon: "pen",
|
||||||
text: "Draw",
|
text: "Draw",
|
||||||
|
@ -180,8 +184,18 @@ function EventBar(props: { clear: () => void }) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
draw: [
|
draw: [
|
||||||
{ icon: faBroomWide, text: "Clear", callback: props.clear },
|
{
|
||||||
{ icon: faPalette, text: "Color", iconColor: color() },
|
icon: faBroomWide,
|
||||||
|
text: "Clear",
|
||||||
|
showWhen: selfIsActiveIndex,
|
||||||
|
callback: () => clearDrawing(selfIndex()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: faPalette,
|
||||||
|
text: "Color",
|
||||||
|
showWhen: selfIsActiveIndex,
|
||||||
|
iconColor: color(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: shouldHide() ? faEye : faEyeSlash,
|
icon: shouldHide() ? faEye : faEyeSlash,
|
||||||
text: shouldHide() ? "Show" : "Hide",
|
text: shouldHide() ? "Show" : "Hide",
|
||||||
|
@ -301,10 +315,8 @@ function EventBar(props: { clear: () => void }) {
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<For each={items()[gameProps.menu]}>
|
<For each={items()[gameProps.menu]}>
|
||||||
{(e, i) => (
|
{(e) => (
|
||||||
<Show
|
<Show when={!e?.showWhen || e?.showWhen()}>
|
||||||
when={selfIsActiveIndex() || gameProps.menu !== "main" || i() !== 1}
|
|
||||||
>
|
|
||||||
<Item {...e} />
|
<Item {...e} />
|
||||||
</Show>
|
</Show>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -7,7 +7,8 @@ import BorderTiles from "~/components/Gamefield/BorderTiles"
|
||||||
import EventBar from "~/components/Gamefield/EventBar"
|
import EventBar from "~/components/Gamefield/EventBar"
|
||||||
import HitElems from "~/components/Gamefield/HitElems"
|
import HitElems from "~/components/Gamefield/HitElems"
|
||||||
import Targets from "~/components/Gamefield/Targets"
|
import Targets from "~/components/Gamefield/Targets"
|
||||||
import { DrawingCanvas, clearDrawing } from "~/hooks/useDraw"
|
import { DrawingCanvas } from "~/hooks/useDraw"
|
||||||
|
import { setFrameSize } from "~/hooks/useDrawProps"
|
||||||
import {
|
import {
|
||||||
full,
|
full,
|
||||||
gameProps,
|
gameProps,
|
||||||
|
@ -29,6 +30,7 @@ import Ships from "./Ships"
|
||||||
export const count = 12
|
export const count = 12
|
||||||
|
|
||||||
function Gamefield() {
|
function Gamefield() {
|
||||||
|
let frameRef: HTMLDivElement
|
||||||
const { ships } = useSession()
|
const { ships } = useSession()
|
||||||
const navigator = useNavigate()
|
const navigator = useNavigate()
|
||||||
const { isConnected } = useSocket()
|
const { isConnected } = useSocket()
|
||||||
|
@ -93,6 +95,16 @@ function Gamefield() {
|
||||||
reset()
|
reset()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
function handleResize() {
|
||||||
|
const rect = frameRef.getBoundingClientRect()
|
||||||
|
setFrameSize({ x: rect.width, y: rect.height })
|
||||||
|
}
|
||||||
|
handleResize()
|
||||||
|
window.addEventListener("resize", handleResize)
|
||||||
|
onCleanup(() => removeEventListener("resize", handleResize))
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="gamefield">
|
<div id="gamefield">
|
||||||
{/* <Bluetooth /> */}
|
{/* <Bluetooth /> */}
|
||||||
|
@ -102,6 +114,7 @@ function Gamefield() {
|
||||||
onMouseLeave={() =>
|
onMouseLeave={() =>
|
||||||
setMouseCursor((e) => ({ ...e, shouldShow: false }))
|
setMouseCursor((e) => ({ ...e, shouldShow: false }))
|
||||||
}
|
}
|
||||||
|
ref={frameRef!}
|
||||||
>
|
>
|
||||||
<BorderTiles />
|
<BorderTiles />
|
||||||
|
|
||||||
|
@ -118,7 +131,7 @@ function Gamefield() {
|
||||||
|
|
||||||
<DrawingCanvas />
|
<DrawingCanvas />
|
||||||
</div>
|
</div>
|
||||||
<EventBar clear={clearDrawing} />
|
<EventBar />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,33 @@
|
||||||
import { createEffect, createSignal, onCleanup } from "solid-js"
|
import { createEffect, createSignal, onCleanup } from "solid-js"
|
||||||
import { socket } from "~/lib/socket"
|
import { socket } from "~/lib/socket"
|
||||||
import { Draw, DrawLineProps, PlayerEvent, Point } from "../interfaces/frontend"
|
import { DrawLineProps, PlayerEvent, Point } from "../interfaces/frontend"
|
||||||
import { color, enable, shouldHide } from "./useDrawProps"
|
import { color, enable, frameSize, shouldHide } from "./useDrawProps"
|
||||||
|
import { useSession } from "./useSession"
|
||||||
|
|
||||||
let canvasRef: HTMLCanvasElement
|
let canvasRef: HTMLCanvasElement
|
||||||
|
const strokes: Record<0 | 1, DrawLineProps[]> = { 0: [], 1: [] }
|
||||||
|
|
||||||
|
function drawLine(
|
||||||
|
{ prevPoint, currentPoint, color }: DrawLineProps,
|
||||||
|
ctx: CanvasRenderingContext2D,
|
||||||
|
i: 0 | 1,
|
||||||
|
) {
|
||||||
|
strokes[i].push({ prevPoint, currentPoint, color })
|
||||||
|
|
||||||
|
const currX = currentPoint.x * frameSize().x
|
||||||
|
const currY = currentPoint.y * frameSize().y
|
||||||
|
|
||||||
|
const startPoint = prevPoint ?? currentPoint
|
||||||
|
const startX = startPoint.x * frameSize().x
|
||||||
|
const startY = startPoint.y * frameSize().y
|
||||||
|
|
||||||
function drawLine({ prevPoint, currentPoint, ctx, color }: Draw) {
|
|
||||||
const { x: currX, y: currY } = currentPoint
|
|
||||||
const lineColor = color
|
const lineColor = color
|
||||||
const lineWidth = 5
|
const lineWidth = 5
|
||||||
|
|
||||||
const startPoint = prevPoint ?? currentPoint
|
|
||||||
ctx.beginPath()
|
ctx.beginPath()
|
||||||
ctx.lineWidth = lineWidth
|
ctx.lineWidth = lineWidth
|
||||||
ctx.strokeStyle = lineColor
|
ctx.strokeStyle = lineColor
|
||||||
ctx.moveTo(startPoint.x, startPoint.y)
|
ctx.moveTo(startX, startY)
|
||||||
ctx.lineTo(currX, currY)
|
ctx.lineTo(currX, currY)
|
||||||
ctx.stroke()
|
ctx.stroke()
|
||||||
|
|
||||||
|
@ -24,10 +37,12 @@ function drawLine({ prevPoint, currentPoint, ctx, color }: Draw) {
|
||||||
ctx.fill()
|
ctx.fill()
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear(sIndex?: { i: 0 | 1 }) {
|
||||||
const canvas = canvasRef
|
const canvas = canvasRef
|
||||||
if (!canvas) return
|
if (!canvas) return
|
||||||
|
|
||||||
|
if (sIndex) strokes[sIndex.i] = []
|
||||||
|
|
||||||
const ctx = canvas.getContext("2d")
|
const ctx = canvas.getContext("2d")
|
||||||
if (!ctx) return
|
if (!ctx) return
|
||||||
|
|
||||||
|
@ -36,40 +51,51 @@ function clear() {
|
||||||
|
|
||||||
export function DrawingCanvas() {
|
export function DrawingCanvas() {
|
||||||
const [mouseDown, setMouseDown] = createSignal(false)
|
const [mouseDown, setMouseDown] = createSignal(false)
|
||||||
|
const [stateIndex, setStateIndex] = createSignal<{ i: 0 | 1 } | null>(null)
|
||||||
let prevPoint: null | Point
|
const { selfIndex, selfIsActiveIndex, activeIndex } = useSession()
|
||||||
|
|
||||||
const onMouseDown = () => setMouseDown(true)
|
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
const i = activeIndex()
|
||||||
|
const canvas = canvasRef
|
||||||
|
if (!canvas || i === stateIndex()?.i) return
|
||||||
|
|
||||||
|
const ctx = canvasRef?.getContext("2d")
|
||||||
|
if (!ctx) return
|
||||||
|
|
||||||
|
clear()
|
||||||
|
strokes[i].forEach((props) => drawLine(props, ctx, i))
|
||||||
|
setStateIndex({ i })
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
let prevPoint: null | Point
|
||||||
|
|
||||||
const canvas = canvasRef
|
const canvas = canvasRef
|
||||||
if (!canvas) return
|
if (!canvas) return
|
||||||
|
|
||||||
const handler = (e: MouseEvent) => {
|
const handler = (e: MouseEvent) => {
|
||||||
if (!mouseDown()) return
|
const sIndex = selfIndex()
|
||||||
const currentPoint = computePointInCanvas(e)
|
if (!mouseDown() || !selfIsActiveIndex() || !sIndex) return
|
||||||
|
|
||||||
|
const rect = canvas.getBoundingClientRect()
|
||||||
|
const x = (e.clientX - rect.left) / frameSize().x
|
||||||
|
const y = (e.clientY - rect.top) / frameSize().y
|
||||||
|
|
||||||
|
const currentPoint = { x, y }
|
||||||
|
|
||||||
const ctx = canvasRef?.getContext("2d")
|
const ctx = canvasRef?.getContext("2d")
|
||||||
if (!ctx || !currentPoint) return
|
if (!ctx) return
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
currentPoint,
|
currentPoint,
|
||||||
prevPoint: prevPoint,
|
prevPoint,
|
||||||
color: color(),
|
color: color(),
|
||||||
}
|
}
|
||||||
socket.emit("draw-line", props)
|
socket.emit("draw-line", props)
|
||||||
drawLine({ ctx, ...props })
|
drawLine(props, ctx, sIndex.i)
|
||||||
prevPoint = currentPoint
|
prevPoint = currentPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
const computePointInCanvas = (e: MouseEvent) => {
|
|
||||||
const rect = canvas.getBoundingClientRect()
|
|
||||||
const x = e.clientX - rect.left
|
|
||||||
const y = e.clientY - rect.top
|
|
||||||
|
|
||||||
return { x, y }
|
|
||||||
}
|
|
||||||
|
|
||||||
const mouseUpHandler = () => {
|
const mouseUpHandler = () => {
|
||||||
setMouseDown(false)
|
setMouseDown(false)
|
||||||
prevPoint = null
|
prevPoint = null
|
||||||
|
@ -87,30 +113,32 @@ export function DrawingCanvas() {
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
const sIndex = selfIndex()
|
||||||
const canvas = canvasRef
|
const canvas = canvasRef
|
||||||
if (!canvas) return
|
if (!canvas || !sIndex) return
|
||||||
|
|
||||||
const ctx = canvas.getContext("2d")
|
const ctx = canvas.getContext("2d")
|
||||||
if (!ctx) return
|
if (!ctx) return
|
||||||
|
|
||||||
const playerEvent = (event: PlayerEvent) => {
|
const playerEvent = (event: PlayerEvent) => {
|
||||||
if (!canvasRef?.toDataURL() || event.type !== "connect") return
|
if (
|
||||||
console.log("sending canvas state")
|
!strokes[sIndex.i].length ||
|
||||||
socket.emit("canvas-state", canvasRef.toDataURL())
|
!selfIsActiveIndex() ||
|
||||||
|
event.type !== "connect"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
console.log("Sending canvas state.")
|
||||||
|
socket.emit("canvas-state", strokes[sIndex.i])
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvasStateFromServer = (state: string) => {
|
const canvasStateFromServer = (state: DrawLineProps[], i: 0 | 1) => {
|
||||||
console.log("I received the state")
|
console.log("Canvas state received.")
|
||||||
const img = new Image()
|
clear({ i })
|
||||||
img.src = state
|
state.forEach((props) => drawLine(props, ctx, i))
|
||||||
img.onload = () => {
|
|
||||||
ctx?.drawImage(img, 0, 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const socketDrawLine = (props: DrawLineProps) => {
|
const socketDrawLine = (props: DrawLineProps, i: 0 | 1) => {
|
||||||
if (!ctx) return console.log("no ctx here")
|
drawLine(props, ctx, i)
|
||||||
drawLine({ ctx, ...props })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.on("playerEvent", playerEvent)
|
socket.on("playerEvent", playerEvent)
|
||||||
|
@ -130,18 +158,22 @@ export function DrawingCanvas() {
|
||||||
<canvas
|
<canvas
|
||||||
style={{
|
style={{
|
||||||
opacity: shouldHide() ? 0 : 1,
|
opacity: shouldHide() ? 0 : 1,
|
||||||
"box-shadow": enable() ? "inset 0 0 0 2px " + color() : "none",
|
"box-shadow":
|
||||||
|
enable() && selfIsActiveIndex()
|
||||||
|
? "inset 0 0 0 2px " + color()
|
||||||
|
: "none",
|
||||||
"pointer-events": enable() && !shouldHide() ? "auto" : "none",
|
"pointer-events": enable() && !shouldHide() ? "auto" : "none",
|
||||||
}}
|
}}
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
onMouseDown={onMouseDown}
|
onMouseDown={() => setMouseDown(true)}
|
||||||
width="648"
|
width={frameSize().x}
|
||||||
height="648"
|
height={frameSize().y}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearDrawing() {
|
export function clearDrawing(sIndex: { i: 0 | 1 } | null) {
|
||||||
clear()
|
if (!sIndex) return
|
||||||
|
clear(sIndex)
|
||||||
socket.emit("canvas-clear")
|
socket.emit("canvas-clear")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { createSignal } from "solid-js"
|
import { createSignal } from "solid-js"
|
||||||
|
import { Position } from "~/interfaces/frontend"
|
||||||
|
|
||||||
export const colors = [
|
export const colors = [
|
||||||
"#ff4400",
|
"#ff4400",
|
||||||
|
@ -22,6 +23,10 @@ export const colors = [
|
||||||
export const [enable, setEnable] = createSignal(false)
|
export const [enable, setEnable] = createSignal(false)
|
||||||
export const [shouldHide, setShouldHide] = createSignal(false)
|
export const [shouldHide, setShouldHide] = createSignal(false)
|
||||||
export const [color, setColor] = createSignal("#b32aa9")
|
export const [color, setColor] = createSignal("#b32aa9")
|
||||||
|
export const [frameSize, setFrameSize] = createSignal<Position>({
|
||||||
|
x: 648,
|
||||||
|
y: 648,
|
||||||
|
})
|
||||||
|
|
||||||
export function reset() {
|
export function reset() {
|
||||||
setEnable(false)
|
setEnable(false)
|
||||||
|
|
|
@ -26,9 +26,9 @@ export interface ServerToClientEvents {
|
||||||
isReady: (payload: { i: 0 | 1; isReady: boolean }) => void
|
isReady: (payload: { i: 0 | 1; isReady: boolean }) => void
|
||||||
isConnected: (payload: { i: 0 | 1; isConnected: boolean }) => void
|
isConnected: (payload: { i: 0 | 1; isConnected: boolean }) => void
|
||||||
"get-canvas-state": () => void
|
"get-canvas-state": () => void
|
||||||
"canvas-state-from-server": (state: string, userIndex: 0 | 1) => void
|
"canvas-state-from-server": (state: DrawLineProps[], i: 0 | 1) => void
|
||||||
"draw-line": (props: DrawLineProps, userIndex: 0 | 1) => void
|
"draw-line": (props: DrawLineProps, i: 0 | 1) => void
|
||||||
"canvas-clear": () => void
|
"canvas-clear": (index: { i: 0 | 1 }) => void
|
||||||
gameState: (newState: GameState) => void
|
gameState: (newState: GameState) => void
|
||||||
ships: (ships: ShipProps[], index: 0 | 1) => void
|
ships: (ships: ShipProps[], index: 0 | 1) => void
|
||||||
dispatchMove: (props: MoveDispatchProps, i: 0 | 1) => void
|
dispatchMove: (props: MoveDispatchProps, i: 0 | 1) => void
|
||||||
|
@ -42,7 +42,7 @@ export interface ClientToServerEvents {
|
||||||
join: (withAck: (ack: boolean) => void) => void
|
join: (withAck: (ack: boolean) => void) => void
|
||||||
gameSetting: (payload: GameSettings, callback: (hash: string) => void) => void
|
gameSetting: (payload: GameSettings, callback: (hash: string) => void) => void
|
||||||
leave: (withAck: (ack: boolean) => void) => void
|
leave: (withAck: (ack: boolean) => void) => void
|
||||||
"canvas-state": (state: string) => void
|
"canvas-state": (state: DrawLineProps[]) => void
|
||||||
"draw-line": (props: DrawLineProps) => void
|
"draw-line": (props: DrawLineProps) => void
|
||||||
"canvas-clear": () => void
|
"canvas-clear": () => void
|
||||||
gameState: (newState: GameState) => void
|
gameState: (newState: GameState) => void
|
||||||
|
|
|
@ -31,6 +31,7 @@ export interface ItemProps {
|
||||||
amount?: number
|
amount?: number
|
||||||
iconColor?: string
|
iconColor?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
showWhen?: () => boolean
|
||||||
enabled?: boolean
|
enabled?: boolean
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
}
|
}
|
||||||
|
@ -54,9 +55,6 @@ export interface DrawLineProps {
|
||||||
prevPoint: Point | null
|
prevPoint: Point | null
|
||||||
color: string
|
color: string
|
||||||
}
|
}
|
||||||
export interface Draw extends DrawLineProps {
|
|
||||||
ctx: CanvasRenderingContext2D
|
|
||||||
}
|
|
||||||
export interface ShipProps extends Position {
|
export interface ShipProps extends Position {
|
||||||
size: number
|
size: number
|
||||||
variant: number
|
variant: number
|
||||||
|
|
|
@ -210,8 +210,8 @@ export async function GET({
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("canvas-clear", () => {
|
socket.on("canvas-clear", () => {
|
||||||
if (!socket.data.gameId) return
|
if (!socket.data.gameId || !socket.data.index) return
|
||||||
socket.to(socket.data.gameId).emit("canvas-clear")
|
socket.to(socket.data.gameId).emit("canvas-clear", socket.data.index)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("gameState", async (newState) => {
|
socket.on("gameState", async (newState) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue