diff --git a/app/components/Settings/BottomScreen/Menus/User/Snake.vue b/app/components/Settings/BottomScreen/Menus/User/Snake.vue index b8239ce..ece497e 100644 --- a/app/components/Settings/BottomScreen/Menus/User/Snake.vue +++ b/app/components/Settings/BottomScreen/Menus/User/Snake.vue @@ -289,29 +289,99 @@ onRender((ctx) => { } } else { // food + const foodPos = cellToBoardPos(food); ctx.fillStyle = "#ff2020"; - ctx.fillRect( - cellToBoardPos(food).x + 1, - cellToBoardPos(food).y + 1, - CELL_SIZE, - CELL_SIZE, - ); + ctx.fillRect(foodPos.x + 1, foodPos.y + 1, CELL_SIZE, CELL_SIZE); // snake - for (const part of tail) { - ctx.fillStyle = state.value === "dead" ? "#991010" : "#20ff20"; - ctx.fillRect( - cellToBoardPos(part).x + 1, - cellToBoardPos(part).y + 1, - CELL_SIZE, - CELL_SIZE, - ); + const snakeParts = [...tail, position]; + const dead = state.value === "dead"; + const FILL = dead ? "#b84800" : "#ff8c00"; + const FILL_DIM = dead ? "#8c3600" : "#cc7000"; + + const connectedTo = (i: number, dx: number, dy: number) => { + const part = snakeParts[i]!; + const neighbor = new THREE.Vector2(part.x + dx, part.y + dy); + const prev = snakeParts[i - 1]; + const next = snakeParts[i + 1]; + return (prev && prev.equals(neighbor)) || (next && next.equals(neighbor)); + }; + + // fills + for (let i = 0; i < snakeParts.length; i++) { + const part = snakeParts[i]!; + const { x, y } = cellToBoardPos(part); + const px = x + 1; + const py = y + 1; + + ctx.fillStyle = i === snakeParts.length - 1 ? FILL : FILL_DIM; + ctx.fillRect(px, py, CELL_SIZE, CELL_SIZE); + + if (connectedTo(i, 1, 0)) { + ctx.fillRect(px + CELL_SIZE, py, CELL_PADDING, CELL_SIZE); + } + if (connectedTo(i, 0, 1)) { + ctx.fillRect(px, py + CELL_SIZE, CELL_SIZE, CELL_PADDING); + } } - assets.user.snakeHead.draw( - ctx, - cellToBoardPos(position).x + 1, - cellToBoardPos(position).y, - ); + + // outlines + ctx.fillStyle = "#ffffff"; + for (let i = 0; i < snakeParts.length; i++) { + const part = snakeParts[i]!; + const { x, y } = cellToBoardPos(part); + const px = x + 1; + const py = y + 1; + + if (!connectedTo(i, 0, -1)) { + ctx.fillRect(px, py, CELL_SIZE, 1); + } + if (!connectedTo(i, 0, 1)) { + ctx.fillRect(px, py + CELL_SIZE - 1, CELL_SIZE, 1); + } + if (!connectedTo(i, -1, 0)) { + ctx.fillRect(px, py, 1, CELL_SIZE); + } + if (!connectedTo(i, 1, 0)) { + ctx.fillRect(px + CELL_SIZE - 1, py, 1, CELL_SIZE); + } + } + + const EYE_SIZE = 2; + const EYE_MARGIN = 3; + const eyeFar = CELL_SIZE - EYE_MARGIN - EYE_SIZE; + + const head = snakeParts[snakeParts.length - 1]!; + const { x: headCellX, y: headCellY } = cellToBoardPos(head); + const headX = headCellX + 1; + const headY = headCellY + 1; + + let eye1x: number, eye1y: number, eye2x: number, eye2y: number; + if (direction.x === 1) { + eye1x = headX + eyeFar; + eye1y = headY + EYE_MARGIN; + eye2x = headX + eyeFar; + eye2y = headY + eyeFar; + } else if (direction.x === -1) { + eye1x = headX + EYE_MARGIN; + eye1y = headY + EYE_MARGIN; + eye2x = headX + EYE_MARGIN; + eye2y = headY + eyeFar; + } else if (direction.y === 1) { + eye1x = headX + EYE_MARGIN; + eye1y = headY + eyeFar; + eye2x = headX + eyeFar; + eye2y = headY + eyeFar; + } else { + eye1x = headX + EYE_MARGIN; + eye1y = headY + EYE_MARGIN; + eye2x = headX + eyeFar; + eye2y = headY + EYE_MARGIN; + } + + ctx.fillStyle = dead ? "#3d1500" : "#7a3800"; + ctx.fillRect(eye1x, eye1y, EYE_SIZE, EYE_SIZE); + ctx.fillRect(eye2x, eye2y, EYE_SIZE, EYE_SIZE); } ctx.restore(); @@ -353,7 +423,7 @@ useKeyDown(({ key }) => { break; } - if (newDirection.clone().dot(direction) === 0) { + if (newDirection.dot(direction) === 0) { nextDirection.copy(newDirection); atlas.audio.type.play(0.35); }