| 1 | import { useEffect, useRef } from 'react'; |
| 2 | import type { Prefs } from '../game/prefs'; |
| 3 | import { WIND_EN, WIND_NAMES } from '../game/tiles'; |
| 4 | import type { SeatId } from '../game/types'; |
| 5 | import type { NetSession } from '../net/session'; |
| 6 | import { Bi } from './Bi'; |
| 7 | |
| 8 | const SEATS: SeatId[] = [0, 1, 2, 3]; |
| 9 | |
| 10 | /** |
| 11 | * The room before the deal: who is here, who is sitting where, and — for the |
| 12 | * host — the button that starts it. Seats nobody takes go to the computer, so |
| 13 | * two friends and two bots is a game, not a wait. |
| 14 | */ |
| 15 | export function OnlineLobby({ session, prefs }: { session: NetSession; prefs: Prefs }) { |
| 16 | const seats = session.seats; |
| 17 | const me = session.room.me.id; |
| 18 | |
| 19 | // Walking in unseated: take the first free chair rather than making everyone |
| 20 | // pick one. Moving is one tap on another seat. |
| 21 | const claimed = useRef(false); |
| 22 | useEffect(() => { |
| 23 | if (claimed.current || session.mySeat !== null) return; |
| 24 | if (seats.some((s) => s.id === null)) { |
| 25 | claimed.current = true; |
| 26 | session.claimSeat(null); |
| 27 | } |
| 28 | }, [session, seats]); |
| 29 | |
| 30 | const seated = seats.filter((s) => s.id !== null).length; |
| 31 | |
| 32 | return ( |
| 33 | <div className="lobby"> |
| 34 | <h1>台灣麻將</h1> |
| 35 | <p className="sub">線上對戰 · online game · {session.playerCount} 人在房 in the room</p> |
| 36 | <p className="hint"> |
| 37 | 把這頁的連結傳給朋友,他們一開就進同一房。 |
| 38 | <br /> |
| 39 | Share this page's invite link — anyone who opens it lands at this table. |
| 40 | </p> |
| 41 | |
| 42 | <div className="pick-grid"> |
| 43 | {SEATS.map((i) => { |
| 44 | const slot = seats[i]; |
| 45 | const mine = slot.id === me; |
| 46 | const taken = slot.id !== null && !mine; |
| 47 | return ( |
| 48 | <button |
| 49 | key={i} |
| 50 | className={`btn big-btn ${mine ? '' : 'ghost'}`} |
| 51 | disabled={taken} |
| 52 | onClick={() => !mine && session.claimSeat(i)} |
| 53 | > |
| 54 | {WIND_NAMES[i]} {WIND_EN[i]} |
| 55 | <em> |
| 56 | {slot.id === null |
| 57 | ? '空位,開局時由電腦代打 · open — a bot if nobody sits' |
| 58 | : mine |
| 59 | ? `${slot.name || '你'} · you` |
| 60 | : slot.name} |
| 61 | </em> |
| 62 | </button> |
| 63 | ); |
| 64 | })} |
| 65 | </div> |
| 66 | |
| 67 | <div className="lobby-buttons"> |
| 68 | {session.isHost ? ( |
| 69 | <button |
| 70 | className="btn big-btn" |
| 71 | disabled={seated === 0} |
| 72 | onClick={() => session.startOnline(prefs.rules)} |
| 73 | > |
| 74 | <Bi zh="開局" en="Deal" sub={seated < 4 ? `${4 - seated} 個電腦 bots fill in` : undefined} /> |
| 75 | </button> |
| 76 | ) : ( |
| 77 | <p className="hint">等房主開局 · waiting for the host to deal</p> |
| 78 | )} |
| 79 | </div> |
| 80 | {session.err && <p className="hint">{session.err}</p>} |
| 81 | </div> |
| 82 | ); |
| 83 | } |