adding select role interface for pve demo; fix bugs
This commit is contained in:
parent
b6f0503c9e
commit
edc060c00a
|
@ -3,12 +3,21 @@
|
|||
.doudizhu-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
//background-color: #C3CDFF;
|
||||
position: relative;
|
||||
|
||||
#gameboard-background {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("./images/gameboard.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 125%;
|
||||
background-position: bottom;
|
||||
position: relative;
|
||||
|
||||
&.blur-background {
|
||||
filter: blur(3px);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.played-card-area {
|
||||
font-size: 12px;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Avatar from '@material-ui/core/Avatar';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Chip from '@material-ui/core/Chip';
|
||||
import { Layout } from 'element-react';
|
||||
import React from 'react';
|
||||
import '../../assets/doudizhu.scss';
|
||||
import Landlord_wName from '../../assets/images/Portrait/Landlord_wName.png';
|
||||
|
@ -14,19 +15,19 @@ class DoudizhuGameBoard extends React.Component {
|
|||
return this.props.playerInfo[playerIdx].role === 'landlord' ? (
|
||||
<div>
|
||||
<img src={Landlord_wName} alt={'Landlord'} height="70%" width="70%" />
|
||||
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} clickable color="primary" />
|
||||
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} color="primary" />
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<img src={Peasant_wName} alt={'Peasant'} height="70%" width="70%" />
|
||||
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} clickable color="primary" />
|
||||
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} color="primary" />
|
||||
</div>
|
||||
);
|
||||
} else
|
||||
return (
|
||||
<div>
|
||||
<img src={PlaceHolderPlayer} alt={'Player'} height="70%" width="70%" />
|
||||
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} clickable color="primary" />
|
||||
<Chip avatar={<Avatar>ID</Avatar>} label={playerId} color="primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -90,7 +91,11 @@ class DoudizhuGameBoard extends React.Component {
|
|||
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
||||
return (
|
||||
<li key={`handCard-${card}`}>
|
||||
<label className={`card ${rankClass} ${suitClass}`}>
|
||||
<label
|
||||
className={`card ${
|
||||
this.props.showCardBack ? 'back' : '' + rankClass + ' ' + suitClass
|
||||
}`}
|
||||
>
|
||||
<span className="rank">{rankText}</span>
|
||||
<span className="suit">{suitText}</span>
|
||||
</label>
|
||||
|
@ -107,7 +112,11 @@ class DoudizhuGameBoard extends React.Component {
|
|||
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
||||
return (
|
||||
<li key={`handCard-${card}`}>
|
||||
<label className={`card ${rankClass} ${suitClass}`}>
|
||||
<label
|
||||
className={`card ${
|
||||
this.props.showCardBack ? 'back' : '' + rankClass + ' ' + suitClass
|
||||
}`}
|
||||
>
|
||||
<span className="rank">{rankText}</span>
|
||||
<span className="suit">{suitText}</span>
|
||||
</label>
|
||||
|
@ -209,6 +218,12 @@ class DoudizhuGameBoard extends React.Component {
|
|||
}
|
||||
return (
|
||||
<div className="doudizhu-wrapper" style={{}}>
|
||||
<div
|
||||
id={'gameboard-background'}
|
||||
className={
|
||||
this.props.gameStatus === 'ready' && this.props.gamePlayable ? 'blur-background' : undefined
|
||||
}
|
||||
>
|
||||
<div id={'left-player'}>
|
||||
<div className="player-main-area">
|
||||
<div className="player-info">{this.computePlayerPortrait(leftId, leftIdx)}</div>
|
||||
|
@ -236,7 +251,9 @@ class DoudizhuGameBoard extends React.Component {
|
|||
<div className="played-card-area">{rightIdx >= 0 ? this.playerDecisionArea(rightIdx) : ''}</div>
|
||||
</div>
|
||||
<div id={'bottom-player'}>
|
||||
<div className="played-card-area">{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ''}</div>
|
||||
<div className="played-card-area">
|
||||
{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ''}
|
||||
</div>
|
||||
<div className="player-main-area">
|
||||
<div className="player-info">{this.computePlayerPortrait(bottomId, bottomIdx)}</div>
|
||||
{bottomIdx >= 0 ? (
|
||||
|
@ -251,6 +268,53 @@ class DoudizhuGameBoard extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{this.props.gamePlayable && this.props.gameStatus === 'ready' ? (
|
||||
<Layout.Row
|
||||
type="flex"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => this.props.handleSelectRole('landlord_up')}
|
||||
style={{ width: '220px' }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<img src={Peasant_wName} alt="Peasant" width="48px" />}
|
||||
>
|
||||
Play as Peasant
|
||||
<br />
|
||||
(Early Hand)
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => this.props.handleSelectRole('landlord')}
|
||||
style={{ width: '220px', marginTop: '20px', marginBottom: '20px' }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<img src={Landlord_wName} alt="Peasant" width="48px" />}
|
||||
>
|
||||
Play as Landlord
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => this.props.handleSelectRole('landlord_down')}
|
||||
style={{ width: '220px' }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<img src={Peasant_wName} alt="Peasant" width="48px" />}
|
||||
>
|
||||
Play as Peasant
|
||||
<br />
|
||||
(Late Hand)
|
||||
</Button>
|
||||
</Layout.Row>
|
||||
) : undefined}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import axios from 'axios';
|
|||
import { Layout, Message } from 'element-react';
|
||||
import qs from 'query-string';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import '../../assets/doudizhu.scss';
|
||||
import { DoudizhuGameBoard } from '../../components/GameBoard';
|
||||
import {
|
||||
card2SuiteAndRank,
|
||||
|
@ -28,26 +29,7 @@ const initConsiderationTime = 30000;
|
|||
const considerationTimeDeduction = 1000;
|
||||
const apiPlayDelay = 3000;
|
||||
const mainPlayerId = 0; // index of main player (for the sake of simplify code logic)
|
||||
const playerInfo = [
|
||||
{
|
||||
id: 0,
|
||||
index: 0,
|
||||
role: 'peasant',
|
||||
douzeroPlayerPosition: 1,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
index: 1,
|
||||
role: 'peasant',
|
||||
douzeroPlayerPosition: 2,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
index: 2,
|
||||
role: 'landlord',
|
||||
douzeroPlayerPosition: 0,
|
||||
},
|
||||
];
|
||||
let playerInfo = [];
|
||||
|
||||
let initHands = [
|
||||
shuffledDoudizhuDeck.slice(3, 20),
|
||||
|
@ -57,8 +39,6 @@ let initHands = [
|
|||
console.log('init hands', initHands);
|
||||
console.log('three landlord card', threeLandlordCards);
|
||||
console.log('player info', playerInfo);
|
||||
const landlordIdx = playerInfo.find((player) => player.role === 'landlord').index;
|
||||
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
|
||||
|
||||
let gameStateTimeout = null;
|
||||
|
||||
|
@ -87,10 +67,6 @@ function PvEDoudizhuDemoView() {
|
|||
const [selectedCards, setSelectedCards] = useState([]); // user selected hand card
|
||||
const [isPassDisabled, setIsPassDisabled] = useState(true);
|
||||
|
||||
const cardStr2Arr = (cardStr) => {
|
||||
return cardStr === 'pass' || cardStr === '' ? 'pass' : cardStr.split(' ');
|
||||
};
|
||||
|
||||
const cardArr2DouzeroFormat = (cards) => {
|
||||
return cards
|
||||
.map((card) => {
|
||||
|
@ -190,6 +166,8 @@ function PvEDoudizhuDemoView() {
|
|||
lastMoveLandlordUp = newHistoryRecord;
|
||||
playedCardsLandlordUp = playedCardsLandlordUp.concat(newHistoryRecord);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gameHistory.push(newHistoryRecord);
|
||||
if (isDoudizhuBomb(newHistoryRecord)) bombNum++;
|
||||
|
@ -215,10 +193,11 @@ function PvEDoudizhuDemoView() {
|
|||
gameEndDialogText = winner.role + ' wins!';
|
||||
setIsGameEndDialogOpen(true);
|
||||
}, 300);
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
setConsiderationTime(initConsiderationTime);
|
||||
// manually trigger timer if consideration time equals initConsiderationTime
|
||||
if (initConsiderationTime === considerationTime) gameStateTimer();
|
||||
}
|
||||
};
|
||||
|
||||
const requestApiPlay = async () => {
|
||||
|
@ -345,6 +324,51 @@ function PvEDoudizhuDemoView() {
|
|||
setSelectedCards(newSelectedCards);
|
||||
};
|
||||
|
||||
const handleSelectRole = (role) => {
|
||||
const playerInfoTemplate = [
|
||||
{
|
||||
id: 0,
|
||||
index: 0,
|
||||
role: 'peasant',
|
||||
douzeroPlayerPosition: -1,
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
index: 1,
|
||||
role: 'peasant',
|
||||
douzeroPlayerPosition: -1,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
index: 2,
|
||||
role: 'peasant',
|
||||
douzeroPlayerPosition: -1,
|
||||
},
|
||||
];
|
||||
switch (role) {
|
||||
case 'landlord_up':
|
||||
playerInfo = deepCopy(playerInfoTemplate);
|
||||
playerInfo[1].role = 'landlord';
|
||||
break;
|
||||
case 'landlord':
|
||||
playerInfo = deepCopy(playerInfoTemplate);
|
||||
playerInfo[0].role = 'landlord';
|
||||
break;
|
||||
case 'landlord_down':
|
||||
playerInfo = deepCopy(playerInfoTemplate);
|
||||
playerInfo[2].role = 'landlord';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const landlordIdx = playerInfo.find((player) => player.role === 'landlord').index;
|
||||
playerInfo[landlordIdx].douzeroPlayerPosition = 0;
|
||||
playerInfo[(landlordIdx + 1) % 3].douzeroPlayerPosition = 1;
|
||||
playerInfo[(landlordIdx + 2) % 3].douzeroPlayerPosition = 2;
|
||||
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
|
||||
setGameStatus('playing');
|
||||
};
|
||||
|
||||
const gameStateTimer = () => {
|
||||
gameStateTimeout = setTimeout(() => {
|
||||
let currentConsiderationTime = considerationTime;
|
||||
|
@ -365,12 +389,7 @@ function PvEDoudizhuDemoView() {
|
|||
// todo: proceed next game option
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
gameStateTimer();
|
||||
}, [considerationTime]);
|
||||
|
||||
// set init game state
|
||||
useEffect(() => {
|
||||
const startGame = async () => {
|
||||
// start game
|
||||
setGameStatus('playing');
|
||||
|
||||
|
@ -378,12 +397,33 @@ function PvEDoudizhuDemoView() {
|
|||
// find landord to be the first player
|
||||
newGameState.currentPlayer = playerInfo.find((element) => element.role === 'landlord').index;
|
||||
newGameState.hands = initHands.map((element) => sortDoudizhuCards(element));
|
||||
|
||||
// if first player is user, fetch legal actions
|
||||
if (newGameState.currentPlayer === mainPlayerId) {
|
||||
const player_hand_cards = cardArr2DouzeroFormat(newGameState.hands[mainPlayerId].slice().reverse());
|
||||
let rival_move = '';
|
||||
const requestBody = {
|
||||
player_hand_cards,
|
||||
rival_move,
|
||||
};
|
||||
const apiRes = await axios.post(`${douzeroDemoUrl}/legal`, qs.stringify(requestBody));
|
||||
const data = apiRes.data;
|
||||
legalActions = {
|
||||
turn: 0,
|
||||
actions: data.legal_action.split(','),
|
||||
};
|
||||
}
|
||||
|
||||
setGameState(newGameState);
|
||||
gameStateTimer();
|
||||
}, []);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (gameState.currentPlayer) {
|
||||
gameStateTimer();
|
||||
}, [considerationTime]);
|
||||
|
||||
useEffect(() => {
|
||||
if (gameState.currentPlayer && gameStatus === 'playing') {
|
||||
// if current player is not user, request for API player
|
||||
if (gameState.currentPlayer !== mainPlayerId) {
|
||||
requestApiPlay();
|
||||
|
@ -391,6 +431,10 @@ function PvEDoudizhuDemoView() {
|
|||
}
|
||||
}, [gameState.currentPlayer]);
|
||||
|
||||
useEffect(() => {
|
||||
if (gameStatus === 'playing') startGame();
|
||||
}, [gameStatus]);
|
||||
|
||||
const runNewTurn = () => {};
|
||||
|
||||
const handleMainPlayerAct = (type) => {
|
||||
|
@ -446,7 +490,7 @@ function PvEDoudizhuDemoView() {
|
|||
<DialogContentText id="alert-dialog-description">{gameEndDialogText}</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseGameEndDialog} color="primary" autoFocus>
|
||||
<Button onClick={() => handleCloseGameEndDialog()} color="primary" autoFocus>
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
@ -457,6 +501,8 @@ function PvEDoudizhuDemoView() {
|
|||
<div style={{ height: '100%' }}>
|
||||
<Paper className={'doudizhu-gameboard-paper'} elevation={3}>
|
||||
<DoudizhuGameBoard
|
||||
showCardBack={true}
|
||||
handleSelectRole={handleSelectRole}
|
||||
isPassDisabled={isPassDisabled}
|
||||
gamePlayable={true}
|
||||
playerInfo={playerInfo}
|
||||
|
@ -473,6 +519,7 @@ function PvEDoudizhuDemoView() {
|
|||
gameStatus={gameStatus}
|
||||
handleMainPlayerAct={handleMainPlayerAct}
|
||||
/>
|
||||
{/* )} */}
|
||||
</Paper>
|
||||
</div>
|
||||
</Layout.Col>
|
||||
|
|
Loading…
Reference in New Issue