improve ui

This commit is contained in:
sparshg
2024-09-20 02:51:49 +05:30
parent 8a85437feb
commit 9cc0defa05
6 changed files with 103 additions and 32 deletions

58
app/src/lib/join.svelte Normal file
View 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>

View File

@@ -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';
}
}

View File

@@ -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>

View File

@@ -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