basic main menu, more style improvements

This commit is contained in:
Daniel Bulant 2023-01-19 22:21:38 +01:00
parent d30f029627
commit eec2afb9da
13 changed files with 235 additions and 33 deletions

View file

@ -10,16 +10,7 @@
padding: 0;
height: 100vh;
width: 100vw;
overflow: hidden;
font-family: "Square", "Squareo", monospace;
}
@font-face {
font-family: 'Square';
src: url("/assets/Square.ttf") format("truetype");
}
@font-face {
font-family: 'Squareo';
src: url("/assets/Squareo.ttf") format("truetype");
font-family: 'Roboto', sans-serif;
}
</style>
%sveltekit.head%

View file

@ -1,6 +1,14 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import Move from "./move.svelte";
export var self: 1 | 2 = 1;
export var twoPlayer: boolean = false;
export var selfName: string | null = null;
export var opponentName: string | null = null;
const dispatch = createEventDispatcher();
var classes = [
'top left',
'top middle',
@ -55,9 +63,12 @@
if(moves.find(move => move.i == i && move.j == j))
return;
if(currentContainer !== i) return;
if(twoPlayer && currentPlayer !== self) return;
moves.push({ p: currentPlayer, i, j });
moves = moves;
dispatch("move", { i, j, p: currentPlayer });
updateContainerStates();
}
@ -151,12 +162,12 @@
</div>
<main class:disabled={overallState} class="flex flex-wrap min-h-100vh min-w-full items-center">
<div class="board relative">
<div class="board relative p-8">
{#each classes as className, i}
<div class:hover={hoveredPiece?.i == i} class:disabled={containerStates[i]} class:current={currentContainer === i} class:highlighted={highlightedContainer === i} class="squares-container {className}">
{#each (new Array(9)) as _, j}
{@const move = moves.find(move => move.i == i && move.j == j)}
<div on:click={() => addMove(i, j)} class:hover={hoveredPiece?.i == i && hoveredPiece.j == j} class="square" class:move class:preview={!move} class:cross={move && move.p==1} class:circle={move && move.p==2} on:mouseover={() => hoveredPiece = { i, j }} on:mouseleave={() => { if(hoveredPiece?.i == i && hoveredPiece.j == j) hoveredPiece = null; }}>
<div on:click={() => addMove(i, j)} class:hover={hoveredPiece?.i == i && hoveredPiece.j == j} class="square" class:move class:preview={!move} class:cross={move && move.p==1} class:circle={move && move.p==2} on:mouseover={() => { if(currentContainer == i) hoveredPiece = { i, j } }} on:mouseleave={() => { if(hoveredPiece?.i == i && hoveredPiece.j == j) hoveredPiece = null; }}>
{#if move}
{#if move.p == 1}
<svg width="16" height="16">
@ -230,9 +241,28 @@
</svg>
</div>
{/if}
<div class="absolute top-200 left-0 right-0 text-center">
{#if currentPlayer == 1}
<svg width="16" height="16" class="text-red-500">
<line x1="0" y1="0" x2="100%" y2="100%" stroke="currentColor" stroke-width="2" />
<line x1="100%" y1="0" x2="0" y2="100%" stroke="currentColor" stroke-width="2" />
</svg>
{:else}
<svg width="16" height="16" class="text-blue-500">
<circle cx="50%" cy="50%" r="45%" stroke="currentColor" stroke-width="2" fill="none" />
</svg>
{/if}
is on turn.
{#if twoPlayer && self == currentPlayer}
<b>It is <span class:text-red-500={currentPlayer == 1} class:text-blue-500={currentPlayer == 2}>YOUR</span> {selfName ? "(" + selfName + ")" : ""} turn.</b>
{:else if twoPlayer && self != currentPlayer}
Waiting for {opponentName || "opponent"}...
{/if}
</div>
</div>
<div class="info min-w-60 px-4 min-h-full">
<div class="info min-w-38 px-4 h-full overflow-y-auto <md:w-full">
<div class="moves">
{#each moves as move}
<Move player={move.p} board={move.i} piece={move.j} on:mouseover={() => hoveredPiece = { i: move.i, j: move.j }} on:mouseout={() => { if(hoveredPiece?.j == move.j && hoveredPiece.i == move.i) hoveredPiece = null }} />
@ -245,6 +275,9 @@
<style>
.info .moves {
columns: 9.5rem auto;
}
.winner {
@apply absolute inset-4 pointer-events-none;
}
@ -264,7 +297,7 @@
@apply grid grid-cols-3 grid-rows-3 gap-10 w-max h-max m-auto my-5;
}
.squares-container {
@apply grid grid-cols-3 grid-rows-3 gap-5 w-max h-max bg-black/5 opacity-35 relative;
@apply grid grid-cols-3 grid-rows-3 gap-5 w-max h-max opacity-35 relative;
}
.square {
@ -291,10 +324,10 @@
.square:hover, .square.hover {
@apply bg-black/5;
}
.square.hover.cross {
.square.hover.cross, .square:hover.cross {
@apply text-red-500;
}
.square.hover.circle {
.square.hover.circle, .square:hover.circle {
@apply text-blue-500;
}
.highlighted {

View file

@ -24,7 +24,7 @@
<line x1="0" y1="66%" x2="100%" y2="66%" stroke="currentColor" stroke-width="2" />
{#if board !== "?"}
<rect x="{[0,33,66][Math.floor(board / 3)]}%" y="{[0,33,66][(board % 3)]}%" width="33%" height="33%" fill="currentColor" stroke="none" />
<rect y="{[0,33,66][Math.floor(board / 3)]}%" x="{[0,33,66][(board % 3)]}%" width="33%" height="33%" fill="currentColor" stroke="none" />
{/if}
</svg></span>
<span class="piece flex items-center">#{piece == "?" ? "?" : piece + 1}<svg width="16" height="16">
@ -34,7 +34,7 @@
<line x1="0" y1="66%" x2="100%" y2="66%" stroke="currentColor" stroke-width="2" />
{#if piece !== "?"}
<rect x="{[0,33,66][Math.floor(piece / 3)]}%" y="{[0,33,66][(piece % 3)]}%" width="33%" height="33%" fill="currentColor" stroke="none" />
<rect y="{[0,33,66][Math.floor(piece / 3)]}%" x="{[0,33,66][(piece % 3)]}%" width="33%" height="33%" fill="currentColor" stroke="none" />
{/if}
</svg></span>
</div>

View file

@ -0,0 +1,14 @@
<script>
import { fly } from "svelte/transition";
export let url = "";
</script>
{#key url}
<div
in:fly={{ x: -5, duration: 500, delay: 300 }}
out:fly={{ x: 5, duration: 500 }}
class="absolute bg-inherit w-full"
>
<slot />
</div>
{/key}

View file

@ -1,5 +1,20 @@
<script>
<script lang="ts">
import PageTransition from "$lib/pageTransition.svelte";
import "uno.css";
import type { load } from "./+layout";
export let data: Awaited<ReturnType<typeof load>>;
</script>
<slot />
<div class="overflow-hidden w-100vw">
<PageTransition url={data.url}>
<slot />
</PageTransition>
</div>
<style>
:global(:root), :global(body) {
@apply overflow-hidden w-100vw;
}
</style>

View file

@ -0,0 +1,6 @@
/** @type {import('./$types').PageLoad} */
export const load = async ({ url }) => {
return {
url: url.pathname
}
};

View file

@ -1,14 +1,81 @@
<script>
import Game from "$lib/view/game/game.svelte";
import List from "$lib/view/menu/list.svelte";
import NameChoose from "$lib/view/menu/nameChoose.svelte";
import { connection, room } from "$lib/Websocket";
</script>
{#if !$connection}
<NameChoose />
{:else if !$room}
<List />
{:else}
<Game />
{/if}
<main class="flex items-center justify-center flex-col">
<div class="chooser">
<a href="/localplay" class="single">
<h1>Local multiplayer</h1>
<img src="/computer.svg" alt="">
<p>A game for two on a single device</p>
</a>
<a href="/multiplayer" class="multi">
<h1>Online multiplayer</h1>
<img src="/network.svg" alt="">
<p>Play with 2 devices, even across the ocean.</p>
</a>
</div>
<div class="rules">
<div class="icon">
<svg fill="currentColor" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="800px" viewBox="0 0 973.1 973.1" xml:space="preserve"
>
<g>
<path d="M502.29,788.199h-47c-33.1,0-60,26.9-60,60v64.9c0,33.1,26.9,60,60,60h47c33.101,0,60-26.9,60-60v-64.9
C562.29,815,535.391,788.199,502.29,788.199z"/>
<path d="M170.89,285.8l86.7,10.8c27.5,3.4,53.6-12.4,63.5-38.3c12.5-32.7,29.9-58.5,52.2-77.3c31.601-26.6,70.9-40,117.9-40
c48.7,0,87.5,12.8,116.3,38.3c28.8,25.6,43.1,56.2,43.1,92.1c0,25.8-8.1,49.4-24.3,70.8c-10.5,13.6-42.8,42.2-96.7,85.9
c-54,43.7-89.899,83.099-107.899,118.099c-18.4,35.801-24.8,75.5-26.4,115.301c-1.399,34.1,25.8,62.5,60,62.5h49
c31.2,0,57-23.9,59.8-54.9c2-22.299,5.7-39.199,11.301-50.699c9.399-19.701,33.699-45.701,72.699-78.1
C723.59,477.8,772.79,428.4,795.891,392c23-36.3,34.6-74.8,34.6-115.5c0-73.5-31.3-138-94-193.4c-62.6-55.4-147-83.1-253-83.1
c-100.8,0-182.1,27.3-244.1,82c-52.8,46.6-84.9,101.8-96.2,165.5C139.69,266.1,152.39,283.5,170.89,285.8z"/>
</g>
</svg>
</div>
<div>
<h1>Rules</h1>
<p>How do I play the game?</p>
</div>
</div>
</main>
<style>
main {
@apply my-4 p-4 w-max m-auto h-100vh;
}
.chooser {
@apply grid grid-cols-2 items-center justify-center gap-8;
}
.chooser > a {
@apply text-black no-underline cursor-pointer w-full p-8 border rounded-lg border-gray-400 border-solid;
}
.chooser > .multi {
@apply cursor-not-allowed text-gray-500;
}
.chooser > .multi img {
@apply opacity-50;
}
.rules {
@apply cursor-not-allowed text-gray-500 flex justify-center items-center w-full my-8 p-4 border rounded-lg border-gray-400 border-solid;
}
.icon {
@apply h-20 w-20 mr-4;
}
.icon svg {
@apply w-full h-full;
}
.chooser h1 {
@apply text-4xl text-center font-bold;
}
.rules h1 {
@apply text-4xl font-bold;
}
p {
@apply text-gray-500 text-center;
}
* {
@apply box-border;
}
</style>

View file

@ -0,0 +1,14 @@
<script>
import Game from "$lib/view/game/game.svelte";
import List from "$lib/view/menu/list.svelte";
import NameChoose from "$lib/view/menu/nameChoose.svelte";
import { connection, room } from "$lib/Websocket";
</script>
{#if !$connection}
<NameChoose />
{:else if !$room}
<List />
{:else}
<Game />
{/if}

3
client/static/circle.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="16" height="16">
<circle cx="50%" cy="50%" r="45%" stroke="rgb(59,130,246)" stroke-width="2" fill="none" />
</svg>

After

Width:  |  Height:  |  Size: 130 B

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
x="0px"
y="0px"
viewBox="0 0 1000 1000"
enable-background="new 0 0 1000 1000"
xml:space="preserve"
id="svg52"
sodipodi:docname="computer.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs56" /><sodipodi:namedview
id="namedview54"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.472"
inkscape:cx="-41.313559"
inkscape:cy="298.72881"
inkscape:window-width="1920"
inkscape:window-height="1011"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg52" />
<g
id="g50"><path
d="M920,62.5H80c-38.7,0-70,31.3-70,70v560c0,38.7,31.3,70,70,70h350v105H255c-19.3,0-35,15.7-35,35v35h560v-35c0-19.3-15.7-35-35-35H570v-105h350c38.7,0,70-31.3,70-70v-560C990,93.8,958.7,62.5,920,62.5L920,62.5L920,62.5z M465,692.5c0-19.3,15.7-35,35-35c19.3,0,35,15.7,35,35c0,19.3-15.7,35-35,35C480.7,727.5,465,711.8,465,692.5L465,692.5L465,692.5z M80,622.5v-490h840v490H80L80,622.5z"
id="path48" /></g>
<path
style="fill:#000000"
d="M 80.148756,622.27644 157.48856,561.60308"
id="path216" /><path
style="fill:#000000;stroke:#000000;stroke-width:23;stroke-dasharray:none;stroke-opacity:1"
d="M 74.905379,624.71086 925.83049,128.83725"
id="path218"
sodipodi:nodetypes="cc" /><g
id="g3201"
transform="matrix(111.02092,0,0,111.02092,201.23721,225.60162)" /></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

5
client/static/cross.svg Normal file
View file

@ -0,0 +1,5 @@
<svg width="16" height="16">
<line x1="0" y1="0" x2="100%" y2="100%" stroke="rgb(239,68,68)" stroke-width="2" />
<line x1="100%" y1="0" x2="0" y2="100%" stroke="rgb(239,68,68)" stroke-width="2" />
</svg>

After

Width:  |  Height:  |  Size: 212 B

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<g><path d="M500,10C229.8,10,10,229.8,10,500c0,270.2,219.8,490,490,490c270.2,0,490-219.8,490-490C990,229.8,770.2,10,500,10z M855.6,294.6H694.2C657,205.3,602.8,136.5,563.4,94.3C688.3,113.7,794.6,189.3,855.6,294.6z M521.8,294.6V114.3c34.4,35.7,86.5,98,124.2,180.3H521.8z M663.7,338.3c15.3,42.6,25.6,89.5,28.1,139.8H521.8V338.3H663.7z M478.2,294.6H354.1c37.7-82.2,89.8-144.5,124.1-180.2V294.6z M478.2,338.3v139.8H308.3c2.5-50.3,12.8-97.2,28.1-139.8H478.2z M264.1,478.1H90c2.6-49.4,13.5-96.6,32.1-139.8h167.5C275.7,381.2,266.3,428,264.1,478.1z M264.1,521.8c2.2,50.2,11.6,97,25.5,139.8H122.1c-18.6-43.3-29.5-90.5-32.1-139.8H264.1z M308.3,521.8h169.9v139.8H336.3C321.1,619.1,310.7,572.2,308.3,521.8z M478.2,705.4v180.3c-34.4-35.7-86.5-98-124.2-180.3H478.2z M521.8,705.4h124.1c-37.7,82.2-89.8,144.5-124.1,180.2V705.4z M521.8,661.7V521.8h169.9c-2.5,50.3-12.8,97.2-28.1,139.8H521.8z M735.9,521.8H910c-2.6,49.4-13.5,96.6-32.1,139.8H710.4C724.3,618.8,733.7,572,735.9,521.8z M735.9,478.1c-2.2-50.2-11.6-97-25.5-139.8h167.5c18.6,43.3,29.5,90.5,32.1,139.8H735.9z M436.6,94.3c-39.4,42.3-93.6,111-130.8,200.3H144.4C205.5,189.3,311.7,113.7,436.6,94.3z M144.4,705.4h161.4c37.2,89.3,91.4,158,130.7,200.3C311.7,886.2,205.5,810.7,144.4,705.4z M563.5,905.7c39.4-42.3,93.5-111,130.7-200.3h161.4C794.6,810.7,688.3,886.2,563.5,905.7z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB