feat(game-14-1): Implement phase 1 foundation
Implements the foundational UI and logic for the 14.1 Endless game mode, as detailed in issue #18. - Adds a new component to handle the specific game view. - Introduces a modal within the game view to select the starting player. - Replaces text input with a button grid for selecting remaining balls. - Updates to correctly initialize the 14.1 game state. Closes #18
This commit is contained in:
113
src/components/GameDetail141.jsx
Normal file
113
src/components/GameDetail141.jsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { h } from 'preact';
|
||||
import { useState } from 'preact/hooks';
|
||||
import styles from './GameDetail.module.css';
|
||||
import modalStyles from './PlayerSelectModal.module.css';
|
||||
|
||||
const StartingPlayerModal = ({ players, onSelect, onCancel }) => (
|
||||
<div className={modalStyles.modalOverlay}>
|
||||
<div className={modalStyles.modalContent} onClick={e => e.stopPropagation()}>
|
||||
<div className={modalStyles.modalHeader}>
|
||||
<h3>Welcher Spieler fängt an?</h3>
|
||||
{/* A cancel button isn't strictly needed if a choice is mandatory */}
|
||||
</div>
|
||||
<div className={modalStyles.playerList}>
|
||||
{players.map((player, index) => (
|
||||
<button key={player.name} className={modalStyles.playerItem} onClick={() => onSelect(index)}>
|
||||
{player.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const GameDetail141 = ({ game, onUpdate, onBack }) => {
|
||||
const handleSelectStartingPlayer = (playerIndex) => {
|
||||
onUpdate({
|
||||
...game,
|
||||
currentPlayer: playerIndex,
|
||||
});
|
||||
};
|
||||
|
||||
// If no player is selected yet, show the modal
|
||||
if (game.currentPlayer === null || game.currentPlayer === undefined) {
|
||||
return <StartingPlayerModal players={game.players} onSelect={handleSelectStartingPlayer} />;
|
||||
}
|
||||
|
||||
const currentPlayer = game.players[game.currentPlayer];
|
||||
|
||||
const handleTurnEnd = (remainingBalls) => {
|
||||
if (remainingBalls > game.ballsOnTable) {
|
||||
console.error("Cannot leave more balls than are on the table.");
|
||||
return;
|
||||
}
|
||||
|
||||
const ballsPotted = game.ballsOnTable - remainingBalls;
|
||||
const newScore = currentPlayer.score + ballsPotted;
|
||||
|
||||
const updatedPlayers = game.players.map((p, index) =>
|
||||
index === game.currentPlayer ? { ...p, score: newScore } : p
|
||||
);
|
||||
|
||||
const nextPlayer = (game.currentPlayer + 1) % game.players.length;
|
||||
|
||||
onUpdate({
|
||||
...game,
|
||||
players: updatedPlayers,
|
||||
ballsOnTable: remainingBalls,
|
||||
currentPlayer: nextPlayer,
|
||||
history: [...game.history, { player: currentPlayer.name, ballsPotted, newScore, ballsOnTable: remainingBalls }],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles['game-detail']}>
|
||||
<div className={styles['game-title']}>
|
||||
14/1 endlos | Race to {game.raceTo}
|
||||
</div>
|
||||
|
||||
<div className={styles['scores-container']}>
|
||||
{game.players.map((p, idx) => (
|
||||
<div
|
||||
className={`${styles['player-score']} ${idx === game.currentPlayer ? styles['active-player'] : ''}`}
|
||||
key={p.name}
|
||||
>
|
||||
<span className={styles['player-name']}>{p.name}</span>
|
||||
<span className={styles['score']}>{p.score}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className={styles['turn-indicator']}>
|
||||
Aktueller Spieler: <strong>{currentPlayer.name}</strong> ({game.ballsOnTable} Bälle auf dem Tisch)
|
||||
</div>
|
||||
|
||||
<div className={styles['potted-balls-container']}>
|
||||
<p className={styles['potted-balls-header']}>Bälle am Ende der Aufnahme:</p>
|
||||
<div className={styles['potted-balls-grid']}>
|
||||
{Array.from({ length: 16 }, (_, i) => i).map(num => (
|
||||
<button
|
||||
key={num}
|
||||
onClick={() => handleTurnEnd(num)}
|
||||
disabled={num > game.ballsOnTable}
|
||||
className={styles['potted-ball-btn']}
|
||||
>
|
||||
{num}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Placeholder for future buttons */}
|
||||
<div className={styles['foul-controls']}>
|
||||
<p>Fouls & Re-Racks (nächste Phase)</p>
|
||||
</div>
|
||||
|
||||
<div className={styles['game-detail-controls']}>
|
||||
<button className="btn" onClick={onBack}>Zurück zur Liste</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GameDetail141;
|
||||
Reference in New Issue
Block a user