Add bot to play against.
This commit is contained in:
+4
-2
@@ -13,8 +13,10 @@ async function post(path: string, body: unknown): Promise<Session> {
|
||||
return data as Session
|
||||
}
|
||||
|
||||
export function createGame(name: string): Promise<Session> {
|
||||
return post('/api/games', { name })
|
||||
export type BotDifficulty = 'easy' | 'medium' | 'hard'
|
||||
|
||||
export function createGame(name: string, bot?: BotDifficulty): Promise<Session> {
|
||||
return post('/api/games', bot ? { name, bot } : { name })
|
||||
}
|
||||
|
||||
export function joinGame(code: string, name: string): Promise<Session> {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
import { createGame, joinGame } from '../api'
|
||||
import type { BotDifficulty } from '../api'
|
||||
import type { Session } from '../types'
|
||||
|
||||
const BOT_LEVELS: { value: BotDifficulty; label: string; blurb: string }[] = [
|
||||
{ value: 'easy', label: '🐣 Easy', blurb: 'Learns you the ropes' },
|
||||
{ value: 'medium', label: '🐺 Medium', blurb: 'Puts up a fight' },
|
||||
{ value: 'hard', label: '🦁 Hard', blurb: 'Shows no mercy' },
|
||||
]
|
||||
|
||||
// Home is the create/join screen shown when there's no active session.
|
||||
export function Home({ onSession }: { onSession: (s: Session) => void }) {
|
||||
const [name, setName] = useState('')
|
||||
@@ -50,6 +57,24 @@ export function Home({ onSession }: { onSession: (s: Session) => void }) {
|
||||
Host a new game
|
||||
</button>
|
||||
|
||||
<div className="home-divider">
|
||||
<span>or challenge the computer</span>
|
||||
</div>
|
||||
|
||||
<div className="home-bots">
|
||||
{BOT_LEVELS.map((b) => (
|
||||
<button
|
||||
key={b.value}
|
||||
className="btn btn-secondary"
|
||||
disabled={busy}
|
||||
title={b.blurb}
|
||||
onClick={() => run(() => createGame(name, b.value))}
|
||||
>
|
||||
{b.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="home-divider">
|
||||
<span>or join a friend</span>
|
||||
</div>
|
||||
|
||||
@@ -93,7 +93,13 @@ export function Table({ session, onLeave }: { session: Session; onLeave: () => v
|
||||
key={p.id}
|
||||
className={`topbar-player ${p.seat === view.youSeat ? 'is-you' : ''}`}
|
||||
>
|
||||
<span className={`conn-dot ${p.connected ? 'on' : 'off'}`} />
|
||||
{p.isBot ? (
|
||||
<span className="bot-dot" title="Computer player">
|
||||
🤖
|
||||
</span>
|
||||
) : (
|
||||
<span className={`conn-dot ${p.connected ? 'on' : 'off'}`} />
|
||||
)}
|
||||
<span className="topbar-name">{p.name}</span>
|
||||
<span className="chip">🏆 {p.trophies}</span>
|
||||
{(view.phase === 'shop' || view.phase === 'cleanup') && (
|
||||
@@ -127,7 +133,7 @@ export function Table({ session, onLeave }: { session: Session; onLeave: () => v
|
||||
)}
|
||||
</div>
|
||||
|
||||
{opponents.some((p) => !p.connected) && view.phase !== 'lobby' && (
|
||||
{opponents.some((p) => !p.connected && !p.isBot) && view.phase !== 'lobby' && (
|
||||
<div className="banner banner-warn">An opponent is disconnected…</div>
|
||||
)}
|
||||
{error && <div className="toast">{error}</div>}
|
||||
|
||||
@@ -371,6 +371,17 @@ h3 {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* The three computer-opponent difficulty buttons share the row evenly. */
|
||||
.home-bots {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.home-bots .btn {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.home-error {
|
||||
color: var(--red-soft);
|
||||
font-weight: 700;
|
||||
@@ -479,6 +490,12 @@ h3 {
|
||||
background: var(--red);
|
||||
}
|
||||
|
||||
/* Bots swap the connection dot for a little robot face. */
|
||||
.bot-dot {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* The room code, stamped like a label on a card tray. */
|
||||
.topbar-code {
|
||||
font-family: var(--font-display);
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface PlayerView {
|
||||
trophies: number
|
||||
ready: boolean
|
||||
connected: boolean
|
||||
isBot?: boolean
|
||||
deckSize: number
|
||||
petCount: number
|
||||
deck?: Card[]
|
||||
|
||||
Reference in New Issue
Block a user