feat(ui/battleship): integrate BattleshipBoard and metadata-driven placement/battle flow
- Added metadata state to App and wired incoming match metadata. - Added Fleet + FLEET_ORDER in BattleShipBoard to drive ship placement order. - Added nextShip + nextShipSize calculation for guided placement. - Updated handlePlace and handleShoot to send structured payloads (action + data). - Added lobby/placement/battle status messages. - Updated grids to use shipBoard + shipName/shipSize props instead of generic grid. - Fixed metadata access (state.Metadata vs state.metadata). - Consolidated PlacementGrid usage and disabled it during battle phase. - Added logging for debugging incoming battleship boards.
This commit is contained in:
@@ -16,6 +16,15 @@ interface BattleBoardProps {
|
||||
metadata: Record<string, any>;
|
||||
}
|
||||
|
||||
const Fleet: Record<string, number> = {
|
||||
carrier: 5,
|
||||
battleship: 4,
|
||||
cruiser: 3,
|
||||
submarine: 3,
|
||||
destroyer: 2,
|
||||
};
|
||||
const FLEET_ORDER = ["carrier", "battleship", "cruiser", "submarine", "destroyer"];
|
||||
|
||||
export default function BattleShipBoard({
|
||||
boards,
|
||||
players,
|
||||
@@ -25,49 +34,55 @@ export default function BattleShipBoard({
|
||||
gameOver,
|
||||
metadata,
|
||||
}: BattleBoardProps) {
|
||||
const { matchId, sendMatchData } = useNakama();
|
||||
const { sendMatchData, matchId } = useNakama();
|
||||
|
||||
const myIndex = players.findIndex((p) => p.user_id === myUserId);
|
||||
const oppIndex = myIndex === 0 ? 1 : 0;
|
||||
|
||||
const phase = metadata["phase"] ?? "placement";
|
||||
console.log(metadata, "metadata");
|
||||
const phase = metadata["phase"] ?? "lobby";
|
||||
const isMyTurn = phase === "battle" && turn === myIndex;
|
||||
|
||||
const myShips = boards[`p${myIndex}_ships`]?.grid ?? [];
|
||||
const myShots = boards[`p${myIndex}_shots`]?.grid ?? [];
|
||||
const myShips = boards[`p${myIndex}_ships`]?.grid ?? [[]];
|
||||
const myShots = boards[`p${myIndex}_shots`] ?.grid ?? [[]];
|
||||
|
||||
const placed = metadata[`p${myIndex}_placed`] ?? 0;
|
||||
|
||||
// ----------- SEND PLACE MESSAGE -----------
|
||||
const nextShip = FLEET_ORDER[placed] || null;
|
||||
const nextShipSize = nextShip ? Fleet[nextShip] : null;
|
||||
|
||||
// ------------------- PLACE SHIP -------------------
|
||||
function handlePlace(ship: string, r: number, c: number, dir: "h" | "v") {
|
||||
sendMatchData(matchId!, 2, {
|
||||
sendMatchData(matchId!, 1, {
|
||||
action: "place",
|
||||
ship,
|
||||
data: {
|
||||
ship: ship,
|
||||
row: r,
|
||||
col: c,
|
||||
dir,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ----------- SEND SHOT MESSAGE -----------
|
||||
// ------------------- SHOOT -------------------
|
||||
function handleShoot(r: number, c: number) {
|
||||
sendMatchData(matchId!, 2, {
|
||||
sendMatchData(matchId!, 1, {
|
||||
action: "shoot",
|
||||
row: r,
|
||||
col: c,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// ------------------- STATUS LINE ---------------------
|
||||
// ------------------- STATUS LABEL -------------------
|
||||
const status = useMemo(() => {
|
||||
if (winner) return `Winner: Player ${winner}`;
|
||||
if (phase === "lobby") return `In Lobby`;
|
||||
if (winner !== null) return `Winner: Player ${winner}`;
|
||||
if (gameOver) return "Game over — draw";
|
||||
if (phase === "placement") return "Place your ships";
|
||||
if (phase === "placement") return `Place your ${nextShip ?? ""}`;
|
||||
if (myIndex === -1) return "Spectating";
|
||||
if (!isMyTurn) return "Opponent's turn";
|
||||
if (!isMyTurn) return "Opponent’s turn";
|
||||
return "Your turn";
|
||||
}, [winner, gameOver, phase, isMyTurn, myIndex]);
|
||||
}, [winner, gameOver, phase, isMyTurn, myIndex, nextShip]);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
@@ -79,20 +94,20 @@ export default function BattleShipBoard({
|
||||
<h2 style={{ marginBottom: 8 }}>{status}</h2>
|
||||
|
||||
{/* ---------------- PHASE 1: PLACEMENT ---------------- */}
|
||||
{phase === "placement" && (
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
{phase === "placement" && nextShip && (
|
||||
<PlacementGrid
|
||||
grid={myShips}
|
||||
placed={placed}
|
||||
shipBoard={myShips}
|
||||
shipName={nextShip}
|
||||
shipSize={nextShipSize}
|
||||
onPlace={handlePlace}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ---------------- PHASE 2: BATTLE ---------------- */}
|
||||
{phase === "battle" && (
|
||||
<>
|
||||
<h3>Your Shots</h3>
|
||||
|
||||
<ShotGrid
|
||||
grid={myShots}
|
||||
isMyTurn={isMyTurn}
|
||||
@@ -102,9 +117,10 @@ export default function BattleShipBoard({
|
||||
|
||||
<h3 style={{ marginTop: "18px" }}>Your Ships</h3>
|
||||
<PlacementGrid
|
||||
grid={myShips}
|
||||
placed={999} // show ships but disable placement
|
||||
readOnly
|
||||
shipBoard={myShips}
|
||||
shipName="readonly"
|
||||
shipSize={0}
|
||||
onPlace={() => {}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user