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