summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjbion <joffrey.bion@amadeus.com>2019-02-24 01:50:26 +0100
committerjbion <joffrey.bion@amadeus.com>2019-02-24 01:50:26 +0100
commit3c8b01f3ab690362268aace634e574c1a7b63888 (patch)
treea8c955ebd9b1f5290422c725f68f50e7067d0e09
parentImprove GameScene by showing the hand (diff)
downloadseven-wonders-3c8b01f3ab690362268aace634e574c1a7b63888.tar.gz
seven-wonders-3c8b01f3ab690362268aace634e574c1a7b63888.tar.bz2
seven-wonders-3c8b01f3ab690362268aace634e574c1a7b63888.zip
Add prepare move actions
-rw-r--r--frontend/src/api/model.js7
-rw-r--r--frontend/src/components/game/GameScene.jsx51
-rw-r--r--frontend/src/components/game/Hand.css31
-rw-r--r--frontend/src/components/game/Hand.jsx52
-rw-r--r--frontend/src/redux/actions/game.js13
-rw-r--r--frontend/src/sagas/game.js8
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerTurnInfo.kt3
-rw-r--r--game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt2
8 files changed, 104 insertions, 63 deletions
diff --git a/frontend/src/api/model.js b/frontend/src/api/model.js
index f5c3481b..54be5cf8 100644
--- a/frontend/src/api/model.js
+++ b/frontend/src/api/model.js
@@ -79,9 +79,14 @@ export type ApiPlayerTurnInfo = {
action: ApiAction,
hand: ApiHandCard[],
neighbourGuildCards: ApiTableCard[],
- message: string
+ message: string,
+ wonderBuildability: ApiWonderBuildability,
};
+export type ApiWonderBuildability = {
+ buildable: boolean
+}
+
export type ApiMoveType = "PLAY" | "PLAY_FREE" | "UPGRADE_WONDER" | "DISCARD" | "COPY_GUILD";
export type ApiProvider = "LEFT_NEIGHBOUR" | "RIGHT_NEIGHBOUR";
export type ApiResourceType = "WOOD" | "STONE" | "ORE" | "CLAY" | "GLASS" | "PAPYRUS" | "LOOM";
diff --git a/frontend/src/components/game/GameScene.jsx b/frontend/src/components/game/GameScene.jsx
index cf3c91f8..0437c567 100644
--- a/frontend/src/components/game/GameScene.jsx
+++ b/frontend/src/components/game/GameScene.jsx
@@ -2,16 +2,14 @@ import { Button, Classes, Intent } from '@blueprintjs/core';
import { List } from 'immutable';
import React, { Component } from 'react';
import { connect } from 'react-redux';
-import type { ApiHandCard, ApiPlayerTurnInfo } from '../../api/model';
+import type { ApiPlayerMove, ApiPlayerTurnInfo } from '../../api/model';
import { Game } from '../../models/games';
import { Player } from '../../models/players';
import { actions } from '../../redux/actions/game';
import { getCurrentTurnInfo } from '../../redux/currentGame';
import { getCurrentGame } from '../../redux/games';
-
import { getCurrentPlayer, getPlayers } from '../../redux/players';
import { Hand } from './Hand';
-
import './GameScene.css'
type GameSceneProps = {
@@ -19,44 +17,42 @@ type GameSceneProps = {
currentPlayer: Player,
players: List<Player>,
turnInfo: ApiPlayerTurnInfo,
- sayReady: () => void
-}
-
-type GameSceneState = {
- selectedCard: ApiHandCard | void
+ sayReady: () => void,
+ prepareMove: (move: ApiPlayerMove) => void,
}
-class GameScenePresenter extends Component<GameSceneProps, GameSceneState> {
-
- state = {
- selectedCard: null
- };
-
- selectCard(c: ApiHandCard) {
- this.setState({selectedCard: c})
- }
+class GameScenePresenter extends Component<GameSceneProps> {
render() {
return (
<div className='gameSceneRoot fullscreen'>
- <h2>Now playing!</h2>
- <p>{this.props.turnInfo ? this.props.turnInfo.message : 'Click "ready" when you are'}</p>
-
- {this.props.turnInfo && <Hand cards={this.props.turnInfo.hand}
- selectedCard={this.state.selectedCard}
- onClick={(c) => this.selectCard(c)}/>}
-
- {!this.props.turnInfo && <Button text="READY" className={Classes.LARGE} intent={Intent.PRIMARY} icon='play' onClick={this.props.sayReady} />}
+ <h1>{this.props.game.name}</h1>
+ {!this.props.turnInfo && <GamePreStart onReadyClicked={this.props.sayReady}/>}
+ {this.props.turnInfo && this.turnInfoScene()}
- <h3>Turn Info</h3>
+ <h4 style={{marginTop: '2rem'}}>Debug</h4>
<div>
<pre>{JSON.stringify(this.props.turnInfo, null, 2) }</pre>
</div>
</div>
);
}
+
+ turnInfoScene() {
+ return <div>
+ <p>{this.props.turnInfo.message}</p>
+ <Hand cards={this.props.turnInfo.hand}
+ wonderUpgradable={this.props.turnInfo.wonderBuildability.buildable}
+ prepareMove={this.props.prepareMove}/>
+ </div>
+ }
}
+const GamePreStart = ({onReadyClicked}) => <div>
+ <p>Click "ready" when you are</p>
+ <Button text="READY" className={Classes.LARGE} intent={Intent.PRIMARY} icon='play' onClick={onReadyClicked} />
+</div>;
+
const mapStateToProps: (state) => GameSceneProps = state => {
const game = getCurrentGame(state.get('games'));
console.info(game);
@@ -65,12 +61,13 @@ const mapStateToProps: (state) => GameSceneProps = state => {
game: game,
currentPlayer: getCurrentPlayer(state),
players: game ? getPlayers(state.get('players'), game.players) : new List(),
- turnInfo: getCurrentTurnInfo(state.get('currentGame'))
+ turnInfo: getCurrentTurnInfo(state.get('currentGame')),
};
};
const mapDispatchToProps = {
sayReady: actions.sayReady,
+ prepareMove: actions.prepareMove,
};
export const GameScene = connect(mapStateToProps, mapDispatchToProps)(GameScenePresenter);
diff --git a/frontend/src/components/game/Hand.css b/frontend/src/components/game/Hand.css
index de7b556d..fb6ccd55 100644
--- a/frontend/src/components/game/Hand.css
+++ b/frontend/src/components/game/Hand.css
@@ -3,24 +3,33 @@
}
.hand-card {
+ align-items: flex-end;
+ display: grid;
margin: 0.2rem;
}
-.hand-card-unplayable {
- opacity: 0.7;
-}
-
-.hand-card-img-selected {
- border: 2px solid #0054ff;
-}
-
-.hand-card-img {
+.hand-card .hand-card-img {
border-radius: 0.5rem;
box-shadow: 0 0 10px black;
+ grid-row: 1;
+ grid-column: 1;
+ opacity: 1;
width: 11rem;
}
-
-.hand-card-img:hover {
+.hand-card.unplayable .hand-card-img {
+ opacity: 0.7;
+}
+.hand-card:hover .hand-card-img {
box-shadow: 0 10px 40px black;
width: 14rem;
}
+
+.hand-card .action-buttons {
+ align-items: flex-end;
+ display: none;
+ grid-row: 1;
+ grid-column: 1;
+}
+.hand-card:hover .action-buttons {
+ display: flex;
+}
diff --git a/frontend/src/components/game/Hand.jsx b/frontend/src/components/game/Hand.jsx
index 7dea23de..2b8ca196 100644
--- a/frontend/src/components/game/Hand.jsx
+++ b/frontend/src/components/game/Hand.jsx
@@ -1,34 +1,44 @@
+import { Button, ButtonGroup, Classes, Intent } from '@blueprintjs/core';
import React from 'react';
-import type { ApiHandCard } from '../../api/model';
+import type { ApiHandCard, ApiPlayerMove } from '../../api/model';
import './Hand.css'
+type HandProps = {
+ cards: ApiHandCard[],
+ wonderUpgradable: boolean,
+ prepareMove: (move: ApiPlayerMove) => void
+}
+
+export const Hand = ({cards, wonderUpgradable, prepareMove}: HandProps) => {
+ return <div className='hand'>{cards.map((c, i) => <HandCard key={i} card={c}
+ wonderUpgradable={wonderUpgradable}
+ prepareMove={prepareMove}/>)}</div>;
+};
+
type HandCardProps = {
card: ApiHandCard,
- isSelected: boolean,
- onClick: () => void
+ wonderUpgradable: boolean,
+ prepareMove: (move: ApiPlayerMove) => void
}
-const HandCard = ({card, isSelected, onClick}: HandCardProps) => {
- let playableClass = card.playability.playable ? '' : 'hand-card-unplayable';
- let selectedClass = isSelected ? 'hand-card-img-selected' : '';
- return <div className={`hand-card ${playableClass}`}
- onClick={() => card.playability.playable && onClick()}
- aria-disabled={!card.playability.playable}>
+const HandCard = ({card, wonderUpgradable, prepareMove}: HandCardProps) => {
+ let playableClass = card.playability.playable ? '' : 'unplayable';
+ return <div className={`hand-card ${playableClass}`}>
<img src={`/images/cards/${card.image}`}
title={card.name}
alt={'Card ' + card.name}
- className={`hand-card-img ${selectedClass}`}/>
+ className="hand-card-img"/>
+ <ActionButtons card={card} wonderUpgradable={wonderUpgradable} prepareMove={prepareMove} />
</div>
};
-type HandProps = {
- cards: ApiHandCard[],
- selectedCard: ApiHandCard,
- onClick: (card: ApiHandCard) => void
-}
-
-export const Hand = ({cards, selectedCard, onClick}: HandProps) => {
- return <div className='hand'>{cards.map((c, i) => <HandCard key={i} card={c}
- isSelected={selectedCard === c}
- onClick={() => onClick(c)}/>)}</div>;
-}
+const ActionButtons = ({card, wonderUpgradable, prepareMove}) => <ButtonGroup className="action-buttons">
+ <Button title="PLAY" className={Classes.LARGE} intent={Intent.SUCCESS} icon='play'
+ disabled={!card.playability.playable}
+ onClick={() => prepareMove({type: 'PLAY', cardName: card.name, boughtResources: []})}/>
+ <Button title="BUILD WONDER" className={Classes.LARGE} intent={Intent.PRIMARY} icon='key-shift'
+ disabled={!wonderUpgradable}
+ onClick={() => prepareMove({type: 'UPGRADE_WONDER', cardName: card.name, boughtResources: []})}/>
+ <Button title="DISCARD" className={Classes.LARGE} intent={Intent.DANGER} icon='cross'
+ onClick={() => prepareMove({type: 'DISCARD', cardName: card.name, boughtResources: []})}/>
+</ButtonGroup>;
diff --git a/frontend/src/redux/actions/game.js b/frontend/src/redux/actions/game.js
index 1fe49dfb..434aa8d3 100644
--- a/frontend/src/redux/actions/game.js
+++ b/frontend/src/redux/actions/game.js
@@ -1,7 +1,8 @@
-import type { ApiPlayerTurnInfo, ApiPreparedCard, ApiTable } from '../../api/model';
+import type { ApiPlayerMove, ApiPlayerTurnInfo, ApiPreparedCard, ApiTable } from '../../api/model';
export const types = {
REQUEST_SAY_READY: 'GAME/REQUEST_SAY_READY',
+ REQUEST_PREPARE_MOVE: 'GAME/REQUEST_PREPARE_MOVE',
PLAYER_READY_RECEIVED: 'GAME/PLAYER_READY_RECEIVED',
TABLE_UPDATE_RECEIVED: 'GAME/TABLE_UPDATE_RECEIVED',
PREPARED_CARD_RECEIVED: 'GAME/PREPARED_CARD_RECEIVED',
@@ -9,15 +10,23 @@ export const types = {
};
export type SayReadyAction = { type: 'GAME/REQUEST_SAY_READY' };
+export type PrepareMoveAction = { type: 'GAME/REQUEST_PREPARE_MOVE', move: ApiPlayerMove };
export type PlayerReadyEvent = { type: 'GAME/PLAYER_READY_RECEIVED', username: string };
export type TableUpdateEvent = { type: 'GAME/TABLE_UPDATE_RECEIVED', table: ApiTable };
export type PreparedCardEvent = { type: 'GAME/PREPARED_CARD_RECEIVED', card: ApiPreparedCard };
export type TurnInfoEvent = { type: 'GAME/TURN_INFO_RECEIVED', turnInfo: ApiPlayerTurnInfo };
-export type GameAction = SayReadyAction | PlayerReadyEvent | TableUpdateEvent | PreparedCardEvent | TurnInfoEvent;
+export type GameAction =
+ SayReadyAction
+ | PrepareMoveAction
+ | PlayerReadyEvent
+ | TableUpdateEvent
+ | PreparedCardEvent
+ | TurnInfoEvent;
export const actions = {
sayReady: () => ({ type: types.REQUEST_SAY_READY }),
+ prepareMove: (move: ApiPlayerMove) => ({ type: types.REQUEST_PREPARE_MOVE, move }),
receivePlayerReady: (username: string) => ({ type: types.PLAYER_READY_RECEIVED, username }),
receiveTableUpdate: (table: ApiTable) => ({ type: types.TABLE_UPDATE_RECEIVED, table }),
receivePreparedCard: (card: ApiPreparedCard) => ({ type: types.PREPARED_CARD_RECEIVED, card }),
diff --git a/frontend/src/sagas/game.js b/frontend/src/sagas/game.js
index 04209e91..dc92e89a 100644
--- a/frontend/src/sagas/game.js
+++ b/frontend/src/sagas/game.js
@@ -49,6 +49,13 @@ function* sayReady(session: SevenWondersSession) {
}
}
+function* prepareMove(session: SevenWondersSession) {
+ while (true) {
+ let action = yield take(types.REQUEST_PREPARE_MOVE);
+ yield apply(session, session.prepareMove, [action.move]);
+ }
+}
+
function* watchTurnInfo(session: SevenWondersSession) {
const channel = yield eventChannel(session.watchTurnInfo());
try {
@@ -69,6 +76,7 @@ export function* gameSaga(session: SevenWondersSession) {
call(watchTableUpdates, session, gameId),
call(watchPreparedCards, session, gameId),
call(sayReady, session),
+ call(prepareMove, session),
call(watchTurnInfo, session)
];
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerTurnInfo.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerTurnInfo.kt
index e80cdefd..9eccf935 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerTurnInfo.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerTurnInfo.kt
@@ -1,5 +1,7 @@
package org.luxons.sevenwonders.game.api
+import org.luxons.sevenwonders.game.wonders.WonderBuildability
+
enum class Action(val message: String) {
PLAY("Pick the card you want to play or discard."),
PLAY_2("Pick the first card you want to play or discard. Note that you have the ability to play these 2 last cards. You will choose how to play the last one during your next turn."),
@@ -17,4 +19,5 @@ data class PlayerTurnInfo internal constructor(
) {
val currentAge: Int = table.currentAge
val message: String = action.message
+ val wonderBuildability: WonderBuildability = table.boards[playerIndex].wonder.buildability
}
diff --git a/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt b/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt
index 12f867f2..7b5b9411 100644
--- a/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt
+++ b/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt
@@ -71,7 +71,7 @@ class GameTest {
}
private fun createPlayCardMove(turnInfo: PlayerTurnInfo): MoveExpectation {
- val wonderBuildability = turnInfo.table.boards[turnInfo.playerIndex].wonder.buildability
+ val wonderBuildability = turnInfo.wonderBuildability
if (wonderBuildability.isBuildable) {
val transactions = wonderBuildability.cheapestTransactions.first()
return planMove(turnInfo, MoveType.UPGRADE_WONDER, turnInfo.hand.first(), transactions)
bgstack15