stack-the-deck #1
@@ -78,6 +78,15 @@ export default function GamePage() {
|
|||||||
socket.emit('flip-card', flip);
|
socket.emit('flip-card', flip);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const redraw = (cardIndex: number) => {
|
||||||
|
const redraw: ClientUpdate = {
|
||||||
|
gameID,
|
||||||
|
cardIndex,
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.emit('redraw', redraw);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSettings = (gameData: GameUpdate) => {
|
const handleSettings = (gameData: GameUpdate) => {
|
||||||
socket.emit('settings', { gameID, gameData });
|
socket.emit('settings', { gameID, gameData });
|
||||||
};
|
};
|
||||||
@@ -114,6 +123,7 @@ export default function GamePage() {
|
|||||||
position={layout[cardMap[index]]}
|
position={layout[cardMap[index]]}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
flipAction={() => flipCard(cardMap[index])}
|
flipAction={() => flipCard(cardMap[index])}
|
||||||
|
redrawAction={() => redraw(cardMap[index])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,9 +17,17 @@ type CardProps = {
|
|||||||
position: Layout;
|
position: Layout;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
flipAction: () => void;
|
flipAction: () => void;
|
||||||
|
redrawAction: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Card({ dm, card, position, settings, flipAction }: CardProps) {
|
export default function Card({
|
||||||
|
dm,
|
||||||
|
card,
|
||||||
|
position,
|
||||||
|
settings,
|
||||||
|
flipAction,
|
||||||
|
redrawAction,
|
||||||
|
}: CardProps) {
|
||||||
const [tooltip, setTooltip] = useState<React.ReactNode>(null);
|
const [tooltip, setTooltip] = useState<React.ReactNode>(null);
|
||||||
const { aria, flipped } = card;
|
const { aria, flipped } = card;
|
||||||
|
|
||||||
@@ -71,8 +79,8 @@ export default function Card({ dm, card, position, settings, flipAction }: CardP
|
|||||||
/>
|
/>
|
||||||
{dm && !flipped && (
|
{dm && !flipped && (
|
||||||
<StackTheDeck
|
<StackTheDeck
|
||||||
onRedo={() => console.log('Redo')}
|
onRedraw={redrawAction}
|
||||||
onPick={() => console.log('Pick')}
|
onSelect={() => console.log('Pick')}
|
||||||
onHover={setTooltip}
|
onHover={setTooltip}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { GalleryHorizontalEnd, RefreshCw } from 'lucide-react';
|
import { GalleryHorizontalEnd, RefreshCw } from 'lucide-react';
|
||||||
|
|
||||||
interface StackTheDeckProps {
|
interface StackTheDeckProps {
|
||||||
onRedo: () => void;
|
onRedraw: () => void;
|
||||||
onPick: () => void;
|
onSelect: () => void;
|
||||||
onHover: (state: React.ReactNode) => void;
|
onHover: (state: React.ReactNode) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StackTheDeck({
|
export default function StackTheDeck({
|
||||||
onRedo,
|
onRedraw,
|
||||||
onPick,
|
onSelect,
|
||||||
onHover,
|
onHover,
|
||||||
className = '',
|
className = '',
|
||||||
}: StackTheDeckProps) {
|
}: StackTheDeckProps) {
|
||||||
@@ -28,7 +28,7 @@ export default function StackTheDeck({
|
|||||||
onTouchStart={() => onHover(<p className="text-yellow-400">Redraw</p>)}
|
onTouchStart={() => onHover(<p className="text-yellow-400">Redraw</p>)}
|
||||||
onTouchEnd={() => onHover(null)}
|
onTouchEnd={() => onHover(null)}
|
||||||
className={`p-1 transition-all duration-250 text-yellow-400 hover:text-yellow-300 hover:drop-shadow-[0_0_3px_#ffd700] cursor-pointer`}
|
className={`p-1 transition-all duration-250 text-yellow-400 hover:text-yellow-300 hover:drop-shadow-[0_0_3px_#ffd700] cursor-pointer`}
|
||||||
onClick={curryHandleClick(onRedo)}
|
onClick={curryHandleClick(onRedraw)}
|
||||||
>
|
>
|
||||||
<RefreshCw className="w-3 h-3" />
|
<RefreshCw className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
@@ -39,7 +39,7 @@ export default function StackTheDeck({
|
|||||||
onTouchStart={() => onHover(<p className="text-yellow-400">Select</p>)}
|
onTouchStart={() => onHover(<p className="text-yellow-400">Select</p>)}
|
||||||
onTouchEnd={() => onHover(null)}
|
onTouchEnd={() => onHover(null)}
|
||||||
className={`p-1 transition-all duration-250 text-yellow-400 hover:text-yellow-300 hover:drop-shadow-[0_0_3px_#ffd700] cursor-pointer`}
|
className={`p-1 transition-all duration-250 text-yellow-400 hover:text-yellow-300 hover:drop-shadow-[0_0_3px_#ffd700] cursor-pointer`}
|
||||||
onClick={curryHandleClick(onPick)}
|
onClick={curryHandleClick(onSelect)}
|
||||||
>
|
>
|
||||||
<GalleryHorizontalEnd className="w-3 h-3" />
|
<GalleryHorizontalEnd className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -130,6 +130,19 @@ export default class GameStore {
|
|||||||
return this.gameUpdate(game);
|
return this.gameUpdate(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
redraw(gameID: string, cardIndex: number): GameUpdate {
|
||||||
|
const game = this.getGame(gameID);
|
||||||
|
const card = game.cards[cardIndex];
|
||||||
|
|
||||||
|
if (!card) throw new Error(`Card ${cardIndex} not found`);
|
||||||
|
|
||||||
|
game.cards[cardIndex] =
|
||||||
|
card.suit === 'High Deck' ? deck.drawHigh(game.cards) : deck.drawLow(game.cards);
|
||||||
|
game.lastUpdated = Date.now();
|
||||||
|
|
||||||
|
return this.gameUpdate(game);
|
||||||
|
}
|
||||||
|
|
||||||
updateSettings(gameID: string, settings: Settings) {
|
updateSettings(gameID: string, settings: Settings) {
|
||||||
const game = this.getGame(gameID);
|
const game = this.getGame(gameID);
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,30 @@ export default class TarokkaDeck {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawLow(exclude: TarokkaGameCard[] = []): TarokkaGameCard {
|
||||||
|
const excludeIDs = exclude.map(({ id }) => id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...getRandomItems(
|
||||||
|
this.commonDeck.filter(({ id }) => !excludeIDs.includes(id)),
|
||||||
|
1,
|
||||||
|
)[0],
|
||||||
|
flipped: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
drawHigh(exclude: TarokkaGameCard[] = []): TarokkaGameCard {
|
||||||
|
const excludeIDs = exclude.map(({ id }) => id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...getRandomItems(
|
||||||
|
this.highDeck.filter(({ id }) => !excludeIDs.includes(id)),
|
||||||
|
1,
|
||||||
|
)[0],
|
||||||
|
flipped: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
getBack(): TarokkaCard {
|
getBack(): TarokkaCard {
|
||||||
return this.backs[0];
|
return this.backs[0];
|
||||||
}
|
}
|
||||||
|
|||||||
15
server.ts
15
server.ts
@@ -79,6 +79,21 @@ app.prepare().then(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('redraw', ({ gameID, cardIndex }: ClientUpdate) => {
|
||||||
|
try {
|
||||||
|
//console.log(Date.now(), 'Redraw', { gameID, cardIndex });
|
||||||
|
|
||||||
|
const gameUpdate = gameStore.redraw(gameID, cardIndex);
|
||||||
|
|
||||||
|
broadcast('game-update', gameUpdate);
|
||||||
|
} catch (e) {
|
||||||
|
const error = e instanceof Error ? e.message : e;
|
||||||
|
|
||||||
|
console.error(Date.now(), 'Error[redraw]', error);
|
||||||
|
socket.emit('redraw-error', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
socket.on('settings', ({ gameID, gameData }: { gameID: string; gameData: GameUpdate }) => {
|
socket.on('settings', ({ gameID, gameData }: { gameID: string; gameData: GameUpdate }) => {
|
||||||
try {
|
try {
|
||||||
const gameUpdate = gameStore.updateSettings(gameID, gameData.settings);
|
const gameUpdate = gameStore.updateSettings(gameID, gameData.settings);
|
||||||
|
|||||||
Reference in New Issue
Block a user