improve ui
This commit is contained in:
58
app/src/lib/join.svelte
Normal file
58
app/src/lib/join.svelte
Normal file
@@ -0,0 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { ClipboardCopy } from 'lucide-svelte';
|
||||
|
||||
let joinCode = $state('');
|
||||
|
||||
let {
|
||||
class: className = '',
|
||||
roomCode,
|
||||
createRoom,
|
||||
joinRoom
|
||||
}: {
|
||||
roomCode: string;
|
||||
createRoom: () => void;
|
||||
joinRoom: (code: string) => void;
|
||||
class: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="{className} rounded-lg glass bg-primary flex flex-col items-center justify-center font-mono"
|
||||
>
|
||||
<div class="space-y-4 max-w-[70%]">
|
||||
{#if roomCode}
|
||||
<div class="space-x-2 flex flex-row justify-center items-center">
|
||||
<div class="text-3xl font-bold tracking-widest font-mono bg-accent py-3 rounded-full px-12">
|
||||
{roomCode}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="btn btn-accent btn-circle size-16"
|
||||
onclick={() => navigator.clipboard.writeText(roomCode)}
|
||||
>
|
||||
<ClipboardCopy />
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<button onclick={() => createRoom()} class="w-full btn btn-neutral text-xl">
|
||||
Create Room
|
||||
</button>
|
||||
{/if}
|
||||
<div class="text-center text-lg text-primary-content">OR</div>
|
||||
<div class="space-y-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter code"
|
||||
maxlength="4"
|
||||
bind:value={joinCode}
|
||||
class="input input-bordered input-primary uppercase tracking-widest placeholder-primary text-neutral text-center font-bold text-xl lg:text-3xl w-full glass"
|
||||
/>
|
||||
<button
|
||||
onclick={() => joinRoom(joinCode)}
|
||||
class="w-full btn btn-outline btn-neutral text-neutral hover:text-neutral hover:bg-transparent text-xl"
|
||||
>
|
||||
Join Room
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -1,6 +1,6 @@
|
||||
import { io, Socket } from "socket.io-client";
|
||||
|
||||
export type Phase = 'placement' | 'battle' | 'gameover';
|
||||
export type Phase = 'placement' | 'waiting' | 'selfturn' | 'otherturn';
|
||||
export type CellType = 'e' | 's' | 'h' | 'm'; // empty, ship, hit, miss
|
||||
|
||||
export class State {
|
||||
@@ -16,14 +16,17 @@ export class State {
|
||||
transports: ['websocket']
|
||||
});
|
||||
|
||||
this.socket.on('created-room', (room: string) => {
|
||||
this.socket.on('joined-room', (room: string) => {
|
||||
this.phase = 'waiting';
|
||||
this.room = room;
|
||||
});
|
||||
|
||||
this.socket.on('upload', (_, callback) => {
|
||||
callback(this.playerBoard.board);
|
||||
});
|
||||
this.socket.on('turnover', (id) => {
|
||||
this.turn = id != this.socket.id;
|
||||
this.phase = this.turn ? 'selfturn' : 'otherturn';
|
||||
});
|
||||
this.socket.on('attacked', ({ by, at, hit, sunk }) => {
|
||||
const [i, j]: [number, number] = at;
|
||||
@@ -33,7 +36,17 @@ export class State {
|
||||
} else {
|
||||
this.turn = !hit;
|
||||
}
|
||||
board.board[i][j] = hit ? 'h' : 'm';
|
||||
if (hit) {
|
||||
board.board[i][j] = 'h';
|
||||
for (let [x, y] of [[-1, -1], [1, 1], [1, -1], [-1, 1]]) {
|
||||
const [tx, ty] = [i + x, j + y];
|
||||
if (tx < 0 || tx >= 10 || ty < 0 || ty >= 10) continue;
|
||||
if (board.board[tx][ty] == 'e')
|
||||
board.board[tx][ty] = 'm';
|
||||
}
|
||||
} else {
|
||||
board.board[i][j] = 'm';
|
||||
}
|
||||
if (sunk) {
|
||||
const [[minx, miny], [maxx, maxy]] = sunk;
|
||||
const x1 = Math.max(0, minx - 1);
|
||||
@@ -63,9 +76,14 @@ export class State {
|
||||
this.socket.emit('create');
|
||||
}
|
||||
|
||||
joinRoom() {
|
||||
if (this.room.length != 4) return;
|
||||
this.socket.emit('join', this.room);
|
||||
joinRoom(code: string) {
|
||||
code = code.toUpperCase();
|
||||
if (code.length != 4 || code == this.room) return;
|
||||
this.socket.emit('join', code);
|
||||
}
|
||||
|
||||
hasNotStarted() {
|
||||
return this.phase == 'placement' || this.phase == 'waiting';
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Board from '$lib/board.svelte';
|
||||
import Header from '$lib/header.svelte';
|
||||
import Join from '$lib/join.svelte';
|
||||
import { State } from '$lib/state.svelte';
|
||||
|
||||
const hostname = window.location.hostname;
|
||||
@@ -14,8 +15,8 @@
|
||||
<main class="bg-base-100 shadow-xl rounded-xl overflow-hidden">
|
||||
<div class="p-6 space-y-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 class="text-2xl font-semibold">
|
||||
{gameState.phase === 'placement' ? 'Place Your Ships' : 'Battle Phase'}
|
||||
<h2 class="text-2xl font-semibold rounded-full bg-base-300 py-3 px-6">
|
||||
{gameState.hasNotStarted() ? 'Place your ships' : 'Battle Phase'}
|
||||
</h2>
|
||||
<div class="flex space-x-4">
|
||||
<div class="text-blue-600">Your Ships: {5}</div>
|
||||
@@ -30,28 +31,19 @@
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-medium mb-2">Opponent's Board</h3>
|
||||
<Board board={gameState.opponentBoard} callback={(i, j) => gameState.attack(i, j)} />
|
||||
<div class="relative">
|
||||
<Board board={gameState.opponentBoard} callback={(i, j) => gameState.attack(i, j)} />
|
||||
{#if gameState.hasNotStarted()}
|
||||
<Join
|
||||
class="absolute top-[24px] left-[15px] w-[calc(100%-15px)] h-[calc(100%-24px)]"
|
||||
roomCode={gameState.room}
|
||||
createRoom={() => gameState.createRoom()}
|
||||
joinRoom={(code) => gameState.joinRoom(code)}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center space-x-4">
|
||||
{#if gameState.phase === 'placement'}
|
||||
<button class="btn btn-primary" onclick={() => gameState.playerBoard.randomize()}
|
||||
>Randomize</button
|
||||
>
|
||||
{:else}
|
||||
<button class="btn btn-primary">Fire!</button>
|
||||
{/if}
|
||||
<button class="btn btn-outline" onclick={() => gameState.createRoom()}>Create Room</button
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={gameState.room}
|
||||
placeholder="Code"
|
||||
class="input input-bordered w-full max-w-24 text-center"
|
||||
/>
|
||||
<button class="btn btn-outline" onclick={() => gameState.joinRoom()}>Join Room</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
@@ -9,7 +9,7 @@ export default {
|
||||
],
|
||||
|
||||
daisyui: {
|
||||
themes: ["cupcake", "night"], // false: only light + dark | true: all themes | array: specific themes like this ["light", "dark", "cupcake"]
|
||||
themes: true, // false: only light + dark | true: all themes | array: specific themes like this ["light", "dark", "cupcake"]
|
||||
darkTheme: "cupcake", // name of one of the included themes for dark mode
|
||||
base: true, // applies background color and foreground color for root element by default
|
||||
styled: true, // include daisyUI colors and design decisions for all components
|
||||
|
Reference in New Issue
Block a user