Skip to content

Commit

Permalink
v1 of set draft format (#1515)
Browse files Browse the repository at this point in the history
* checkpoint

* checkpoint - working draft interface

* fix test

* checkpoint - most of the ui is done"

* fixing server

* spectator support

* minor

* minor

* minor

* minor

* minor

* minor

* fix test

* add ability to see drafted cards

* minor
  • Loading branch information
AlexNisnevich authored Jun 26, 2021
1 parent bbbfc2b commit 2598162
Show file tree
Hide file tree
Showing 28 changed files with 839 additions and 222 deletions.
9 changes: 9 additions & 0 deletions src/common/actions/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const SET_SELECTED_CARD_IN_DISCARD_PILE = 'SET_SELECTED_CARD_IN_DISCARD_P
export const SET_SELECTED_TILE = 'SET_SELECTED_TILE';
export const DESELECT = 'DESELECT';
export const ADD_CARD_TO_HAND = 'ADD_CARD_TO_HAND';
export const DRAFT_CARDS = 'DRAFT_CARDS';
export const SET_VOLUME = 'SET_VOLUME';

export function startPractice(format: w.BuiltInFormat, deck: w.CardInStore[]): w.Action {
Expand Down Expand Up @@ -142,6 +143,14 @@ export function addCardToHand(player: w.PlayerColor, card: w.Card): w.Action {
};
}

// (only used in draft format)
export function draftCards(player: w.PlayerColor, cards: w.CardInGame[]): w.Action {
return {
type: DRAFT_CARDS,
payload: { player, cards }
};
}

export function setVolume(volume: number): w.Action {
return {
type: SET_VOLUME,
Expand Down
23 changes: 13 additions & 10 deletions src/common/components/card/CardBack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface CardBackProps {
deckLength?: number
customText?: string
hoverable?: boolean
scale?: number
}

interface CardBackState {
Expand All @@ -17,6 +18,8 @@ export default class CardBack extends React.Component<CardBackProps, CardBackSta
};

public render(): JSX.Element {
const { scale } = this.props;

let style: React.CSSProperties = {};

if (this.props.deckLength) {
Expand All @@ -32,33 +35,33 @@ export default class CardBack extends React.Component<CardBackProps, CardBackSta
onMouseEnter={this.toggleHover}
onMouseLeave={this.toggleHover}
style={{
width: 140,
height: 210,
marginRight: 10,
borderRadius: 5,
width: 140 * (scale || 1),
height: 210 * (scale || 1),
marginRight: 10 * (scale || 1),
borderRadius: 5 * (scale || 1),
backgroundColor: this.state.hover ? '#e91e63' : '#f44336',
boxSizing: 'border-box',
padding: 5,
padding: 5 * (scale || 1),
userSelect: 'none',
cursor: 'pointer', ...style}}
>
<div
style={{
writingMode: 'vertical-lr',
width: 'calc(100% - 50px)',
height: 'calc(100% - 4px)',
width: `calc(100% - ${50 * (scale || 1)}px)`,
height: `calc(100% - ${4 * (scale || 1)}px)`,
display: 'flex',
justifyContent: 'center',
// alignItems: 'center',
paddingLeft: 46,
borderRadius: 5,
paddingLeft: 46 * (scale || 1),
borderRadius: 5 * (scale || 1),
border: '2px solid #FFF'
}}
>
<div
style={{
color: '#fff',
fontSize: 26,
fontSize: 26 * (scale || 1),
fontFamily: 'Carter One'
}}
>
Expand Down
4 changes: 2 additions & 2 deletions src/common/components/game/CardSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Tooltip from '../Tooltip';
import CardSelectorCard from './CardSelectorCard';

interface CardSelectorBaseProps {
onaddCardToHand: (player: string, card: w.CardInStore) => void
onAddCardToHand: (player: string, card: w.CardInStore) => void
cardCollection: w.CardInStore[]
}

Expand Down Expand Up @@ -128,7 +128,7 @@ class CardSelector extends React.Component<CardSelectorProps, CardSelectorState>
private handleGiveCard = (player: string): () => void => (): void => {
const { selectedCard } = this.state;
if (selectedCard) {
this.props.onaddCardToHand(player, selectedCard);
this.props.onAddCardToHand(player, selectedCard);
this.setState({ selectedCard: undefined });
}
}
Expand Down
194 changes: 194 additions & 0 deletions src/common/components/game/DraftArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import { Button, Dialog, DialogActions, DialogContent } from '@material-ui/core';
import * as React from 'react';

import { BACKGROUND_Z_INDEX, BLUE_PLAYER_COLOR, MAX_Z_INDEX, ORANGE_PLAYER_COLOR } from '../../constants';
import * as w from '../../types';
import { opponent } from '../../util/game';

import DraftCardPicker from './DraftCardPicker';
import DraftDeck from './DraftDeck';
import ForfeitButton from './ForfeitButton';
import LeftControls from './LeftControls';
import PlayerName from './PlayerName';

interface DraftAreaProps {
player: w.PlayerColor | 'neither'
usernames: w.PerPlayer<string>
draft: w.DraftState
isGameOver: boolean
volume: number
onForfeit: (winner: w.PlayerColor) => void
onDraftCards: (player: w.PlayerColor, cards: w.CardInGame[]) => void
onSetVolume: (volume: number) => void
onToggleFullscreen: () => void
}

interface DraftAreaState {
isDeckOpen: boolean
}

export default class DraftArea extends React.Component<DraftAreaProps, DraftAreaState> {
public state = {
isDeckOpen: false
};

get currentCardGroup(): w.CardInGame[] | null {
const { draft, player } = this.props;
if (player !== 'neither') {
return draft[player].cardGroupsToShow[0] || null;
} else {
return null;
}
}

public render(): JSX.Element {
const {
player, draft, isGameOver, volume,
onForfeit, onSetVolume, onToggleFullscreen
} = this.props;
const { isDeckOpen } = this.state;

return (
<div
className="background"
style={{
display: 'flex',
height: '100%',
alignItems: 'center',
justifyContent: 'space-between'
}}
>
<div style={{ width: 200 }}>
{this.renderPlayerArea(player === 'neither' ? 'blue' : opponent(player), true)}
<LeftControls
player={player}
currentTurn="draft"
draft={draft}
isTimerEnabled={!isGameOver}
volume={volume}
style={{ marginLeft: 200 }}
onPassTurn={onForfeit}
onSetVolume={onSetVolume}
onToggleFullscreen={onToggleFullscreen}
/>
{this.renderPlayerArea(player === 'neither' ? 'orange' : player, false)}
</div>

<div style={{ width: 500 }}>
{this.renderDraftArea()}
</div>

<div
className="background"
style={{
marginRight: 20,
maxWidth: 220,
textAlign: 'right',
zIndex: BACKGROUND_Z_INDEX
}}
>
<ForfeitButton
text="abort"
width={150}
player={player === 'neither' ? null : player}
isSpectator={player === 'neither'}
gameOver={isGameOver}
onForfeit={onForfeit}
/>
</div>

<Dialog
open={isDeckOpen && !isGameOver}
PaperProps={{
style: {
width: 1010,
maxWidth: 1010,
overflow: 'none'
}
}}
style={{ zIndex: MAX_Z_INDEX }}
onClose={this.handleToggleShowDeck}
>
<DialogContent>
<DraftDeck cards={draft[player as 'blue' | 'orange'].cardsDrafted} />
</DialogContent>
<DialogActions>
<Button
key="Close"
color="primary"
onClick={this.handleToggleShowDeck}
>
Close
</Button>
</DialogActions>
</Dialog>
</div>
);
}

private renderDraftArea(): JSX.Element {
const { player, onDraftCards } = this.props;

if (player === 'neither') {
return (
<div style={{
fontFamily: 'Carter One',
color: 'white',
textAlign: 'center',
fontSize: '2em'
}}>
Waiting for players<br />to draft cards ...
</div>
);
} else if (this.currentCardGroup) {
return <DraftCardPicker cardGroup={this.currentCardGroup} player={player} onDraftCards={onDraftCards} />;
} else {
return (
<div style={{
fontFamily: 'Carter One',
color: 'white',
textAlign: 'center',
fontSize: '2em'
}}>
You&rsquo;re done!<br />Waiting for opponent ...
</div>
);
}
}

private renderPlayerArea(color: w.PlayerColor, isOpponent: boolean): React.ReactNode {
const { draft, usernames } = this.props;

const numCardsDrafted = draft[color].cardsDrafted.length;

return (
<React.Fragment>
<PlayerName
opponent={isOpponent}
color={color}
playerName={usernames[color]}
/>

<div style={{
color: ({orange: ORANGE_PLAYER_COLOR, blue: BLUE_PLAYER_COLOR})[color],
margin: 10,
paddingLeft: 40,
fontSize: '1.5em',
fontFamily: 'VT323',
}}>
{
!isOpponent && numCardsDrafted > 0
? <a className="underline" onClick={this.handleToggleShowDeck}>
{numCardsDrafted}/30 cards
</a>
: <span>{numCardsDrafted}/30 cards</span>
}
</div>
</React.Fragment>
);
}

private handleToggleShowDeck = () => {
this.setState(({ isDeckOpen }) => ({ isDeckOpen: !isDeckOpen }));
}
}
77 changes: 77 additions & 0 deletions src/common/components/game/DraftCardPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { without } from 'lodash';
import * as React from 'react';

import * as w from '../../types';
import { Card } from '../card/Card';

interface DraftCardPickerProps {
cardGroup: w.CardInGame[]
player: w.PlayerColor
onDraftCards: (player: w.PlayerColor, cards: w.CardInGame[]) => void
}

interface DraftCardPickerState {
selectedCardIds: w.CardId[]
}

export default class DraftCardPicker extends React.Component<DraftCardPickerProps, DraftCardPickerState> {
public state: DraftCardPickerState = {
selectedCardIds: []
};

public render(): JSX.Element {
const { cardGroup } = this.props;
const { selectedCardIds } = this.state;

return (
<div>
<div style={{
fontFamily: 'Carter One',
color: 'white',
textAlign: 'center'
}}>
Choose two cards.
</div>
<div style={{
minHeight: '100%',
margin: '0 auto',
display: 'flex',
flexFlow: 'row wrap',
}}>
{cardGroup.map((card, i) => (
<div
key={i}
style={{
display: 'flex',
flexBasis: 'calc(50% - 40px)',
justifyContent: 'center',
flexDirection: 'column',
margin: '0 auto',
}}
>
{Card.fromObj(card, {
selected: selectedCardIds.includes(card.id),
onCardClick: () => this.handleSelectCard(card)
})}
</div>
))}
</div>
</div>
);
}

private handleSelectCard(card: w.CardInGame): void {
const { cardGroup, player, onDraftCards } = this.props;
const { selectedCardIds } = this.state;

const { id } = card;
const newSelectedCardIds = selectedCardIds.includes(id) ? without(selectedCardIds, id) : [...selectedCardIds, id];

if (newSelectedCardIds.length === 2) {
onDraftCards(player, cardGroup.filter((c) => newSelectedCardIds.includes(c.id)));
this.setState({ selectedCardIds: [] });
} else {
this.setState({ selectedCardIds: newSelectedCardIds });
}
}
}
Loading

0 comments on commit 2598162

Please sign in to comment.