restyled doudizhu

This commit is contained in:
songyih 2020-02-23 19:41:00 -08:00
parent febd62404f
commit 6af541d935
20 changed files with 479 additions and 222 deletions

View File

@ -10,13 +10,6 @@
/* card itself
********************************************************************/
.playingCards.unselectable {
pointer-events: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.playingCards {
visibility: visible;
transform: translateY(0) scale(1);
@ -184,7 +177,7 @@
position: absolute;
}
.playingCards .card.joker .rank:before {
content: "\2605";
content: "";
top: 0;
left: 0;
}
@ -193,9 +186,19 @@
}
/* inner multiple suits */
.playingCards .card .suit:after {
.playingCards .card:not(full-content) .suit:after {
display: block;
margin-top: -.8em;
margin-top: -.2em;
text-align: center;
white-space: pre;
line-height: .9;
font-size: 2.5em;
word-spacing: -.05em;
}
.playingCards .card.full-content .suit:after {
display: block;
margin-top: -.9em;
text-align: center;
white-space: pre;
line-height: .9;
@ -204,158 +207,170 @@
}
/* make the hearts and clubs symbols fit, because they are a bit bigger than the others */
.playingCards .card.hearts .suit:after {
.playingCards .card.full-content.hearts .suit:after {
word-spacing: -.15em;
}
.playingCards .card.hearts.rank-10 .suit:after {
.playingCards .card.full-content.hearts.rank-10 .suit:after {
word-spacing: -.05em;
letter-spacing: -.1em;
}
.playingCards .card.clubs.rank-10 .suit:after {
.playingCards .card.full-content.clubs.rank-10 .suit:after {
word-spacing: -.15em;
}
/* 8, 9, 10 are the most crowded */
.playingCards .card.rank-8 .suit:after,
.playingCards .card.rank-9 .suit:after {
.playingCards .card.full-content.rank-8 .suit:after,
.playingCards .card.full-content.rank-9 .suit:after {
letter-spacing: -.075em;
}
.playingCards .card.rank-10 .suit:after {
.playingCards .card.full-content.rank-10 .suit:after {
letter-spacing: -.1em;
}
.playingCards .card.clubs .suit:after {
.playingCards .card.full-content.clubs .suit:after {
letter-spacing: -.125em;
}
/*____________ symbols in the middle (suits, full) ____________*/
/* diamonds */
.playingCards .card.rank-2.diams .suit:after {
.playingCards .card.diams:not(full-content) .suit:after {
content: "\2666";
}
.playingCards .card.rank-2.diams.full-content .suit:after {
content: "\2666 \A\A\2666";
}
.playingCards .card.rank-3.diams .suit:after {
.playingCards .card.rank-3.diams.full-content .suit:after {
content: "\2666 \A\2666 \A\2666";
}
.playingCards .card.rank-4.diams .suit:after {
.playingCards .card.rank-4.diams.full-content .suit:after {
content: "\2666\00A0\00A0\00A0\2666 \A\A\2666\00A0\00A0\00A0\2666";
}
.playingCards .card.rank-5.diams .suit:after {
.playingCards .card.rank-5.diams.full-content .suit:after {
content: "\2666\00A0\00A0\00A0\2666 \A\2666 \A\2666\00A0\00A0\00A0\2666";
}
.playingCards .card.rank-6.diams .suit:after {
.playingCards .card.rank-6.diams.full-content .suit:after {
content: "\2666\00A0\00A0\00A0\2666 \A\2666\00A0\00A0\00A0\2666 \A\2666\00A0\00A0\00A0\2666";
}
.playingCards .card.rank-7.diams .suit:after {
.playingCards .card.rank-7.diams.full-content .suit:after {
content: "\2666\00A0\00A0\2666 \A\2666\00A0\2666\00A0\2666 \A\2666\00A0\00A0\2666";
}
.playingCards .card.rank-8.diams .suit:after {
.playingCards .card.rank-8.diams.full-content .suit:after {
content: "\2666\00A0\2666\00A0\2666 \A\2666\00A0\00A0\2666 \A\2666\00A0\2666\00A0\2666";
}
.playingCards .card.rank-9.diams .suit:after {
.playingCards .card.rank-9.diams.full-content .suit:after {
content: "\2666\00A0\2666\00A0\2666 \A\2666\00A0\2666\00A0\2666 \A\2666\00A0\2666\00A0\2666";
}
.playingCards .card.rank-10.diams .suit:after {
.playingCards .card.rank-10.diams.full-content .suit:after {
content: "\2666\00A0\2666\00A0\2666 \A\2666\00A0\2666\00A0\2666\00A0\2666 \A\2666\00A0\2666\00A0\2666";
}
/* hearts */
.playingCards .card.rank-2.hearts .suit:after {
.playingCards .card.hearts:not(full-content) .suit:after {
content: "\2665";
}
.playingCards .card.rank-2.hearts.full-content .suit:after {
content: "\2665 \A\A\2665";
}
.playingCards .card.rank-3.hearts .suit:after {
.playingCards .card.rank-3.hearts.full-content .suit:after {
content: "\2665 \A\2665 \A\2665";
}
.playingCards .card.rank-4.hearts .suit:after {
.playingCards .card.rank-4.hearts.full-content .suit:after {
content: "\2665\00A0\00A0\00A0\2665 \A\A\2665\00A0\00A0\00A0\2665";
}
.playingCards .card.rank-5.hearts .suit:after {
.playingCards .card.rank-5.hearts.full-content .suit:after {
content: "\2665\00A0\00A0\00A0\2665 \A\2665 \A\2665\00A0\00A0\00A0\2665";
}
.playingCards .card.rank-6.hearts .suit:after {
.playingCards .card.rank-6.hearts.full-content .suit:after {
content: "\2665\00A0\00A0\00A0\2665 \A\2665\00A0\00A0\00A0\2665 \A\2665\00A0\00A0\00A0\2665";
}
.playingCards .card.rank-7.hearts .suit:after {
.playingCards .card.rank-7.hearts.full-content .suit:after {
content: "\2665\00A0\00A0\2665 \A\2665\00A0\2665\00A0\2665 \A\2665\00A0\00A0\2665";
}
.playingCards .card.rank-8.hearts .suit:after {
.playingCards .card.rank-8.hearts.full-content .suit:after {
content: "\2665\00A0\2665\00A0\2665 \A\2665\00A0\00A0\2665 \A\2665\00A0\2665\00A0\2665";
}
.playingCards .card.rank-9.hearts .suit:after {
.playingCards .card.rank-9.hearts.full-content .suit:after {
content: "\2665\00A0\2665\00A0\2665 \A\2665\00A0\2665\00A0\2665 \A\2665\00A0\2665\00A0\2665";
}
.playingCards .card.rank-10.hearts .suit:after {
.playingCards .card.rank-10.hearts.full-content .suit:after {
content: "\2665\00A0\2665\00A0\2665 \A\2665\00A0\2665\00A0\2665\00A0\2665 \A\2665\00A0\2665\00A0\2665";
}
/* spades */
.playingCards .card.rank-2.spades .suit:after {
.playingCards .card.spades:not(full-content) .suit:after {
content: "\2660";
}
.playingCards .card.rank-2.spades.full-content .suit:after {
content: "\2660 \A\A\2660";
}
.playingCards .card.rank-3.spades .suit:after {
.playingCards .card.rank-3.spades.full-content .suit:after {
content: "\2660 \A\2660 \A\2660";
}
.playingCards .card.rank-4.spades .suit:after {
.playingCards .card.rank-4.spades.full-content .suit:after {
content: "\2660\00A0\00A0\00A0\2660 \A\A\2660\00A0\00A0\00A0\2660";
}
.playingCards .card.rank-5.spades .suit:after {
.playingCards .card.rank-5.spades.full-content .suit:after {
content: "\2660\00A0\00A0\00A0\2660 \A\2660 \A\2660\00A0\00A0\00A0\2660";
}
.playingCards .card.rank-6.spades .suit:after {
.playingCards .card.rank-6.spades.full-content .suit:after {
content: "\2660\00A0\00A0\00A0\2660 \A\2660\00A0\00A0\00A0\2660 \A\2660\00A0\00A0\00A0\2660";
}
.playingCards .card.rank-7.spades .suit:after {
.playingCards .card.rank-7.spades.full-content .suit:after {
content: "\2660\00A0\00A0\2660 \A\2660\00A0\2660\00A0\2660 \A\2660\00A0\00A0\2660";
}
.playingCards .card.rank-8.spades .suit:after {
.playingCards .card.rank-8.spades.full-content .suit:after {
content: "\2660\00A0\2660\00A0\2660 \A\2660\00A0\00A0\2660 \A\2660\00A0\2660\00A0\2660";
}
.playingCards .card.rank-9.spades .suit:after {
.playingCards .card.rank-9.spades.full-content .suit:after {
content: "\2660\00A0\2660\00A0\2660 \A\2660\00A0\2660\00A0\2660 \A\2660\00A0\2660\00A0\2660";
}
.playingCards .card.rank-10.spades .suit:after {
.playingCards .card.rank-10.spades.full-content .suit:after {
content: "\2660\00A0\2660\00A0\2660 \A\2660\00A0\2660\00A0\2660\00A0\2660 \A\2660\00A0\2660\00A0\2660";
}
/* clubs */
.playingCards .card.rank-2.clubs .suit:after {
.playingCards .card.clubs:not(full-content) .suit:after {
content: "\2663";
}
.playingCards .card.rank-2.clubs.full-content .suit:after {
content: "\2663 \A\A\2663";
}
.playingCards .card.rank-3.clubs .suit:after {
.playingCards .card.rank-3.clubs.full-content .suit:after {
content: "\2663 \A\2663 \A\2663";
}
.playingCards .card.rank-4.clubs .suit:after {
.playingCards .card.rank-4.clubs.full-content .suit:after {
content: "\2663\00A0\00A0\00A0\2663 \A\A\2663\00A0\00A0\00A0\2663";
}
.playingCards .card.rank-5.clubs .suit:after {
.playingCards .card.rank-5.clubs.full-content .suit:after {
content: "\2663\00A0\00A0\00A0\2663 \A\2663 \A\2663\00A0\00A0\00A0\2663";
}
.playingCards .card.rank-6.clubs .suit:after {
.playingCards .card.rank-6.clubs.full-content .suit:after {
content: "\2663\00A0\00A0\00A0\2663 \A\2663\00A0\00A0\00A0\2663 \A\2663\00A0\00A0\00A0\2663";
}
.playingCards .card.rank-7.clubs .suit:after {
.playingCards .card.rank-7.clubs.full-content .suit:after {
content: "\2663\00A0\00A0\2663 \A\2663\00A0\2663\00A0\2663 \A\2663\00A0\00A0\2663";
}
.playingCards .card.rank-8.clubs .suit:after {
.playingCards .card.rank-8.clubs.full-content .suit:after {
content: "\2663\00A0\2663\00A0\2663 \A\2663\00A0\00A0\2663 \A\2663\00A0\2663\00A0\2663";
}
.playingCards .card.rank-9.clubs .suit:after {
.playingCards .card.rank-9.clubs.full-content .suit:after {
content: "\2663\00A0\2663\00A0\2663 \A\2663\00A0\2663\00A0\2663 \A\2663\00A0\2663\00A0\2663";
}
.playingCards .card.rank-10.clubs .suit:after {
.playingCards .card.rank-10.clubs.full-content .suit:after {
content: "\2663\00A0\2663\00A0\2663 \A\2663\00A0\2663\00A0\2663\00A0\2663 \A\2663\00A0\2663\00A0\2663";
}
/*____________ symbols in the middle (faces as images) ____________*/
.playingCards.faceImages .card.rank-j .suit:after,
.playingCards.faceImages .card.rank-q .suit:after,
.playingCards.faceImages .card.rank-k .suit:after {
.playingCards.faceImages .card.rank-j.full-content .suit:after,
.playingCards.faceImages .card.rank-q.full-content .suit:after,
.playingCards.faceImages .card.rank-k.full-content .suit:after {
content: '';
}
.playingCards.faceImages .card.rank-j,
.playingCards.faceImages .card.rank-q,
.playingCards.faceImages .card.rank-k,
.playingCards.faceImages .card.joker {
.playingCards.faceImages .card.rank-j.full-content,
.playingCards.faceImages .card.rank-q.full-content,
.playingCards.faceImages .card.rank-k.full-content,
.playingCards.faceImages .card.joker.full-content {
background-repeat: no-repeat;
background-position: -1em 0;
/* @change: smaller cards: more negative distance from the left
@ -394,11 +409,11 @@
/*____________ symbols in the middle (faces as dingbat symbols) ____________*/
.playingCards.simpleCards .card .suit:after,
.playingCards .card.rank-j .suit:after,
.playingCards .card.rank-q .suit:after,
.playingCards .card.rank-k .suit:after,
.playingCards .card.rank-a .suit:after,
.playingCards .card.joker .rank:after {
.playingCards .card.rank-j.full-content .suit:after,
.playingCards .card.rank-q.full-content .suit:after,
.playingCards .card.rank-k.full-content .suit:after,
.playingCards .card.rank-a.full-content .suit:after,
.playingCards .card.joker.full-content .rank:after {
font-family: Georgia, serif;
position: absolute;
font-size: 3em;
@ -424,9 +439,12 @@
}
.playingCards .card.joker .rank:after {
position: absolute;
content: "\2766";
top: .4em;
left: .1em;
content: "JOKER";
writing-mode: vertical-lr;
font-weight: bold;
font-size: 0.8em;
top: 0;
left: -0.1em;
}
/* big suits in middle */
@ -519,6 +537,35 @@
.playingCards ul.hand li {
bottom: 0;
}
.playingCards.loose ul.hand li:nth-child(1) { left: 0; }
.playingCards.loose ul.hand li:nth-child(2) { left: 1.4em; }
.playingCards.loose ul.hand li:nth-child(3) { left: 2.8em; }
.playingCards.loose ul.hand li:nth-child(4) { left: 4.2em; }
.playingCards.loose ul.hand li:nth-child(5) { left: 5.6em; }
.playingCards.loose ul.hand li:nth-child(6) { left: 7.0em; }
.playingCards.loose ul.hand li:nth-child(7) { left: 8.4em; }
.playingCards.loose ul.hand li:nth-child(8) { left: 9.8em; }
.playingCards.loose ul.hand li:nth-child(9) { left: 11.2em; }
.playingCards.loose ul.hand li:nth-child(10) { left: 12.6em; }
.playingCards.loose ul.hand li:nth-child(11) { left: 14.0em; }
.playingCards.loose ul.hand li:nth-child(12) { left: 15.4em; }
.playingCards.loose ul.hand li:nth-child(13) { left: 16.8em; }
.playingCards.loose ul.hand li:nth-child(14) { left: 18.2em; }
.playingCards.loose ul.hand li:nth-child(15) { left: 19.6em; }
.playingCards.loose ul.hand li:nth-child(16) { left: 21em; }
.playingCards.loose ul.hand li:nth-child(17) { left: 22.4em; }
.playingCards.loose ul.hand li:nth-child(18) { left: 23.8em; }
.playingCards.loose ul.hand li:nth-child(19) { left: 25.2em; }
.playingCards.loose ul.hand li:nth-child(20) { left: 26.6em; }
.playingCards.loose ul.hand li:nth-child(21) { left: 28em; }
.playingCards.loose ul.hand li:nth-child(22) { left: 29.4em; }
.playingCards.loose ul.hand li:nth-child(23) { left: 30.8em; }
.playingCards.loose ul.hand li:nth-child(24) { left: 32.2em; }
.playingCards.loose ul.hand li:nth-child(25) { left: 33.6em; }
.playingCards.loose ul.hand li:nth-child(26) { left: 35em; }
.playingCards ul.hand li:nth-child(1) { left: 0; }
.playingCards ul.hand li:nth-child(2) { left: 1.1em; }
.playingCards ul.hand li:nth-child(3) { left: 2.2em; }

View File

@ -1,5 +1,14 @@
@import "cards.css";
.doudizhu-wrapper {
width: 100%;
height: 100%;
background-color: #FEF9E7;
background-image: url("./images/table.png");
background-repeat: no-repeat;
background-size: 100% 70%;
background-position: bottom;
position: relative;
.played-card-area {
font-size: 12px;
@ -7,6 +16,37 @@
display: flex;
justify-content: center;
.timer.fade-in {
visibility: hidden;
opacity: 0;
}
.timer.fade-out {
visibility: hidden;
transition: visibility 0.1s, opacity 0.05s;
opacity: 0;
}
.timer {
visibility: visible;
transition: visibility 0s, opacity 0.2s, transform 0.3s;
opacity: 1;
width: 51px;
height: 60px;
.timer-text {
color: #303133;
margin-top: 3px;
font-size: 23px;
font-weight: bold;
text-shadow: 0 2px 2px #909399;
line-height: 57px;
}
text-align: center;
background-image: url("./images/timer.png");
background-repeat: no-repeat;
background-size: 100% 100%;
}
.non-card {
display: table;
width: 280px;
@ -16,7 +56,13 @@
display: table-cell;
vertical-align: middle;
text-align: center;
font-size: 16px;
font-size: 23px;
font-weight: bold;
text-shadow: 0 2px 2px rgba(0, 0, 0, 0.87);
//letter-spacing: -4px;
color: #F2F6FC;
}
}
}
@ -36,14 +82,11 @@
font-size: 16px;
width: 130px;
height: 130px;
border-radius: 25px;
border: 2px solid #73AD21;
display: table;
div {
text-align: center;
}
span {
white-space: pre;
display: table-cell;
vertical-align: middle;
text-align: center;
}
}
@ -175,7 +218,7 @@
}
.non-card {
height: 138px;
height: 70px;
//transform: translateY(-25px);
}
}

View File

@ -1,12 +1,77 @@
.progress-bar {
margin: 5px;
}
.header-bar {
width: 1000px;
margin-left: auto;
margin-right: auto;
padding: 5px;
display: flex;
.stretch {
flex: 1;
}
.github-info {
flex: 0 100px;
cursor: pointer;
transition: color 0.5s ease;
&:hover{
transition: color 0.5s ease;
color: #C0C4CC;
}
.github-icon {
position: absolute;
top: 50%;
-ms-transform: translateY(-50%);
transform: translateY(-50%);
.MuiSvgIcon-root {
font-size: 30px;
}
}
.github-text {
left: 45px;
line-height: 18px;
position: relative;
top: 50%;
font-size: 18px;
-ms-transform: translateY(-50%);
transform: translateY(-50%);
span {
font-size: 12px;
}
}
}
.title {
margin-left: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 26px;
font-weight: 600;
letter-spacing: 1px;
-ms-transform:scale(1, 1.1) translateY(-1px);
-webkit-transform:scale(1, 1.1) translateY(-1px);
-moz-transform:scale(1, 1.1) translateY(-1px);
-o-transform:scale(1, 1.1) translateY(-1px);
.subtitle {
color: #C0C4CC;
margin-left: 11px;
font-size: 16px;
-ms-transform: translateY(3px);
-webkit-transform: translateY(3px);
-moz-transform: translateY(3px);
-o-transform: translateY(3px);
}
}
}
.game-controller {
//width: calc(100% - 20px*2);
//padding: 20px;
//.el-row {
// margin-bottom: 10px;
//}
.game-controller-paper {
margin: 5px;
padding: 2px 10px;
@ -31,6 +96,7 @@
.doudizhu-view-container {
width: 1000px;
margin-top: 5px;
margin-left: auto;
margin-right: auto;

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
src/assets/images/table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

BIN
src/assets/images/timer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -11,3 +11,10 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
.unselectable {
pointer-events: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}

View File

@ -2,7 +2,12 @@ import React from 'react';
import { translateCardData, millisecond2Second, computeHandCardsWidth } from '../../utils'
import '../../assets/doudizhu.scss';
import {fade} from "@material-ui/core";
import Landlord_wName from '../../assets/images/Portrait/Landlord_wName.png';
import Peasant_wName from '../../assets/images/Portrait/Peasant_wName.png';
import PlaceHolderPlayer from '../../assets/images/Portrait/Player.png';
import Chip from '@material-ui/core/Chip';
import Avatar from '@material-ui/core/Avatar';
class DoudizhuGameBoard extends React.Component {
constructor(props) {
@ -10,12 +15,48 @@ class DoudizhuGameBoard extends React.Component {
}
computePlayerPortrait(playerId, playerIdx){
if(this.props.playerInfo.length > 0){
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"
/>
</div>
:
<div>
<img src={Peasant_wName} alt={"Peasant"} height="70%" width="70%" />
<Chip
avatar={<Avatar>ID</Avatar>}
label={playerId}
clickable
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"
/>
</div>
)
}
computeSingleLineHand(cards, fadeClassName="") {
if(cards === "P"){
return <div className="non-card"><span>Pass</span></div>
return <div className="non-card"><span>PASS</span></div>
}else{
return (
<div className={"playingCards unselectable "+fadeClassName}>
<div className={"playingCards unselectable loose "+fadeClassName}>
<ul className="hand" style={{width: computeHandCardsWidth(cards.length, 12)}}>
{cards.map(card=>{
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
@ -46,7 +87,7 @@ class DoudizhuGameBoard extends React.Component {
return (
<div>
<div className="player-hand-up">
<div className="playingCards unselectable">
<div className="playingCards unselectable loose">
<ul className="hand">
{upCards.map(card => {
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
@ -63,7 +104,7 @@ class DoudizhuGameBoard extends React.Component {
</div>
</div>
<div className="player-hand-down">
<div className="playingCards unselectable">
<div className="playingCards unselectable loose">
<ul className="hand">
{downCards.map(card => {
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
@ -84,15 +125,18 @@ class DoudizhuGameBoard extends React.Component {
}
playerDecisionArea(playerIdx){
if(this.props.currentPlayer === playerIdx){
return <div className="non-card"><span>{`Consideration Time: ${millisecond2Second(this.props.considerationTime)}s`}</span></div>
}else{
let fadeClassName = "";
if(this.props.toggleFade === "fade-out" && (playerIdx+2)%3 === this.props.currentPlayer)
fadeClassName = "fade-out";
else if(this.props.toggleFade === "fade-in" && (playerIdx+1)%3 === this.props.currentPlayer)
fadeClassName = "scale-fade-in";
if(this.props.currentPlayer === playerIdx){
return (
<div className={"timer "+fadeClassName}>
<div className="timer-text">{millisecond2Second(this.props.considerationTime)}</div>
</div>
)
}else{
return this.computeSingleLineHand(this.props.latestAction[playerIdx], fadeClassName)
}
}
@ -128,11 +172,11 @@ class DoudizhuGameBoard extends React.Component {
leftId = found.id;
}
return (
<div className="doudizhu-wrapper" style={{width: "100%", height: "100%", backgroundColor: "#ffcc99", position: "relative"}}>
<div className="doudizhu-wrapper" style={{}}>
<div id={"left-player"}>
<div className="player-main-area">
<div className="player-info">
<span>{`Player Id ${leftId}\n${this.props.playerInfo.length > 0 ? this.props.playerInfo[leftIdx].role : ""}`}</span>
{this.computePlayerPortrait(leftId, leftIdx)}
</div>
{leftIdx >= 0 ? this.computeSideHand(this.props.hands[leftIdx]) : <div className="player-hand-placeholder"><span>Waiting...</span></div>}
</div>
@ -143,7 +187,7 @@ class DoudizhuGameBoard extends React.Component {
<div id={"right-player"}>
<div className="player-main-area">
<div className="player-info">
<span>{`Player Id ${rightId}\n${this.props.playerInfo.length > 0 ? this.props.playerInfo[rightIdx].role : ""}`}</span>
{this.computePlayerPortrait(rightId, rightIdx)}
</div>
{rightIdx >= 0 ? this.computeSideHand(this.props.hands[rightIdx]) : <div className="player-hand-placeholder"><span>Waiting...</span></div>}
</div>
@ -157,7 +201,7 @@ class DoudizhuGameBoard extends React.Component {
</div>
<div className="player-main-area">
<div className="player-info">
<span>{`Player Id ${bottomId}\n${this.props.playerInfo.length > 0 ? this.props.playerInfo[bottomIdx].role : ""}`}</span>
{this.computePlayerPortrait(bottomId, bottomIdx)}
</div>
{bottomIdx >= 0 ? <div className="player-hand">{this.computeSingleLineHand(this.props.hands[bottomIdx])}</div> : <div className="player-hand-placeholder"><span>Waiting...</span></div>}
</div>

View File

@ -1,4 +1,3 @@
import React from 'react';
import DoudizhuGameBoard from "./DoudizhuGameBoard";
import LeducHoldemGameBoard from "./LeducHoldemGameBoard";

28
src/components/Navbar.js Normal file
View File

@ -0,0 +1,28 @@
import React, {useState} from "react";
import logo_white from "../assets/images/logo_white.png";
import GitHubIcon from "@material-ui/icons/GitHub";
import AppBar from "@material-ui/core/AppBar";
import axios from 'axios';
export function Navbar(props) {
const [stars, setStars] = useState("...");
axios.get("https://api.github.com/repos/datamllab/rlcard")
.then(res=>{
setStars(res.data.stargazers_count);
});
return (
<AppBar position="static">
<div className={"header-bar"}>
<img src={logo_white} alt={"Logo"} height="65px" />
<div className={"title unselectable"}>Showdown<span className={"subtitle"}>/ {props.gameName}</span></div>
<div className={"stretch"} />
<div className={"github-info"} onClick={()=>{window.location.href = 'https://github.com/datamllab/rlcard'}}>
<div className={"github-icon"}><GitHubIcon /></div>
<div className={"github-text"}>Github<br /><span>{stars} stars</span></div>
</div>
</div>
</AppBar>
)
}
export default Navbar;

View File

@ -2,20 +2,19 @@ import React from 'react';
import axios from 'axios';
import '../assets/gameview.scss';
import { DoudizhuGameBoard } from '../components/GameBoard';
import Navbar from "../components/Navbar";
import {removeCards, doubleRaf, deepCopy, computeHandCardsWidth, translateCardData} from "../utils";
import { Layout } from 'element-react';
import { Layout, Message, Loading } from 'element-react';
import Slider from '@material-ui/core/Slider';
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import Divider from '@material-ui/core/Divider';
import LinearProgress from '@material-ui/core/LinearProgress';
import ButtonGroup from '@material-ui/core/ButtonGroup';
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
import PauseCircleOutlineRoundedIcon from '@material-ui/icons/PauseCircleOutlineRounded';
import ReplayRoundedIcon from '@material-ui/icons/ReplayRounded';
import NotInterestedIcon from '@material-ui/icons/NotInterested';
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
import SkipNextIcon from '@material-ui/icons/SkipNext';
import SkipPreviousIcon from '@material-ui/icons/SkipPrevious';
@ -47,7 +46,8 @@ class DoudizhuGameView extends React.Component {
this.state = {
gameInfo: this.initGameState,
gameStateLoop: null,
gameSpeed: 0
gameSpeed: 0,
fullScreenLoading: false
};
}
@ -72,7 +72,11 @@ class DoudizhuGameView extends React.Component {
if (remainedCards !== false) {
gameInfo.hands[newMove.playerIdx] = remainedCards;
} else {
console.log("Cannot find cards in move from player's hand");
Message({
message: "Cannot find cards in move from player's hand",
type: "error",
showClose: true
});
}
// check if game ends
if(remainedCards.length === 0){
@ -92,7 +96,11 @@ class DoudizhuGameView extends React.Component {
alert("Peasants Win");
}, 200);
}else{
console.log("Error in finding winner");
Message({
message: "Error in finding winner",
type: "error",
showClose: true
});
}
});
return gameInfo;
@ -100,13 +108,21 @@ class DoudizhuGameView extends React.Component {
gameInfo.considerationTime = this.initConsiderationTime;
gameInfo.completedPercent += 100.0 / (this.moveHistory.length - 1);
}else {
console.log("Mismatched current player index");
Message({
message: "Mismatched current player index",
type: "error",
showClose: true
});
}
// if current state is new to game state history, push it to the game state history array
if(gameInfo.turn === this.gameStateHistory.length){
this.gameStateHistory.push(gameInfo);
}else{
console.log("inconsistent game state history length and turn number");
Message({
message: "inconsistent game state history length and turn number",
type: "error",
showClose: true
});
}
}
return gameInfo;
@ -129,7 +145,7 @@ class DoudizhuGameView extends React.Component {
this.gameStateTimer();
}else{
let gameInfo = this.generateNewState();
if(gameInfo.gameStatus == "over") return;
if(gameInfo.gameStatus === "over") return;
gameInfo.gameStatus = "playing";
if(this.state.gameInfo.toggleFade === "fade-out") {
gameInfo.toggleFade = "fade-in";
@ -152,6 +168,8 @@ class DoudizhuGameView extends React.Component {
// for test use
const replayId = 0;
// start full screen loading
this.setState({fullScreenLoading: true});
axios.get(`${this.apiUrl}/replay/doudizhu/${replayId}`)
.then(res => {
res = res.data;
@ -168,7 +186,7 @@ class DoudizhuGameView extends React.Component {
if(this.gameStateHistory.length === 0){ // fix replay bug
this.gameStateHistory.push(gameInfo);
}
this.setState({gameInfo: gameInfo}, ()=>{
this.setState({gameInfo: gameInfo, fullScreenLoading: false}, ()=>{
if(this.gameStateTimeout){
window.clearTimeout(this.gameStateTimeout);
this.gameStateTimeout = null;
@ -176,11 +194,18 @@ class DoudizhuGameView extends React.Component {
// loop to update game state
this.gameStateTimer();
});
})
.catch(()=>{
this.setState({fullScreenLoading: false});
Message({
message: "Error in getting replay data",
type: "error",
showClose: true
});
})
};
runNewTurn(prevTurn){
runNewTurn(){
this.gameStateTimer();
}
@ -225,7 +250,7 @@ class DoudizhuGameView extends React.Component {
return <div className={"non-card "+this.state.gameInfo.toggleFade}><span>Pass</span></div>
}else{
return (
<div className={"unselectable playingCards "+this.state.gameInfo.toggleFade}>
<div className={"unselectable playingCards loose "+this.state.gameInfo.toggleFade}>
<ul className="hand" style={{width: computeHandCardsWidth(cards.length, 10)}}>
{cards.map(card=>{
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
@ -247,7 +272,7 @@ class DoudizhuGameView extends React.Component {
computeProbabilityItem(idx){
if(this.state.gameInfo.gameStatus !== "ready" && this.state.gameInfo.turn < this.moveHistory.length){
let style = {};
style["backgroundColor"] = this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? `rgba(189,183,107,${this.moveHistory[this.state.gameInfo.turn].probabilities[idx].probability})` : "#bdbdbd";
style["backgroundColor"] = this.moveHistory[this.state.gameInfo.turn].probabilities.length > idx ? `rgba(245, 176, 65 , ${this.moveHistory[this.state.gameInfo.turn].probabilities[idx].probability})` : "#bdbdbd";
return (
<div className={"playing"} style={style}>
<div className="probability-move">
@ -322,6 +347,8 @@ class DoudizhuGameView extends React.Component {
];
return (
<div>
<Navbar gameName={"Doudizhu"} />
<div className={"doudizhu-view-container"}>
<Layout.Row style={{"height": "540px"}}>
<Layout.Col style={{"height": "100%"}} span="17">
@ -370,6 +397,7 @@ class DoudizhuGameView extends React.Component {
<div className="progress-bar">
<LinearProgress variant="determinate" value={this.state.gameInfo.completedPercent} />
</div>
<Loading loading={this.state.fullScreenLoading}>
<div className="game-controller">
<Paper className={"game-controller-paper"} elevation={3}>
<Layout.Row style={{"height": "51px"}}>
@ -424,11 +452,8 @@ class DoudizhuGameView extends React.Component {
</Layout.Col>
</Layout.Row>
</Paper>
{/*<Layout.Row>*/}
{/* <Layout.Col span="24">*/}
{/* {`Current Player: ${this.state.gameInfo.currentPlayer} , Consideration Time: ${this.state.gameInfo.considerationTime}, Turn: ${this.state.gameInfo.turn}`}*/}
{/* </Layout.Col>*/}
{/*</Layout.Row>*/}
</div>
</Loading>
</div>
</div>
)

View File

@ -10,12 +10,10 @@ import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import Divider from '@material-ui/core/Divider';
import LinearProgress from '@material-ui/core/LinearProgress';
import ButtonGroup from '@material-ui/core/ButtonGroup';
import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
import PauseCircleOutlineRoundedIcon from '@material-ui/icons/PauseCircleOutlineRounded';
import ReplayRoundedIcon from '@material-ui/icons/ReplayRounded';
import NotInterestedIcon from '@material-ui/icons/NotInterested';
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
import SkipNextIcon from '@material-ui/icons/SkipNext';
import SkipPreviousIcon from '@material-ui/icons/SkipPrevious';
@ -106,7 +104,7 @@ class LeducHoldemGameView extends React.Component {
console.log("Error in player's latest action");
}
gameInfo.turn++;
if(gameInfo.round !== 0 && gameInfo.turn == this.moveHistory[gameInfo.round].length){
if(gameInfo.round !== 0 && gameInfo.turn === this.moveHistory[gameInfo.round].length){
gameInfo.gameStatus = "over";
this.setState({gameInfo: gameInfo});
setTimeout(()=>{
@ -150,7 +148,7 @@ class LeducHoldemGameView extends React.Component {
this.gameStateTimer();
}else{
let gameInfo = this.generateNewState();
if(gameInfo.gameStatus == "over") return;
if(gameInfo.gameStatus === "over") return;
this.gameStateTimer();
gameInfo.gameStatus = "playing";
if(this.state.gameInfo.toggleFade === "fade-out") {
@ -244,7 +242,7 @@ class LeducHoldemGameView extends React.Component {
currentMove = this.moveHistory[this.state.gameInfo.round][this.state.gameInfo.turn];
}
let style = {};
style["backgroundColor"] = currentMove !== null ? `rgba(189,183,107,${currentMove.probabilities[idx].probability})` : "#bdbdbd";;
style["backgroundColor"] = currentMove !== null ? `rgba(189,183,107,${currentMove.probabilities[idx].probability})` : "#bdbdbd";
return (
<div className={"playing"} style={style}>
<div className="probability-move">
@ -262,7 +260,7 @@ class LeducHoldemGameView extends React.Component {
}
go2PrevGameState() {
let gameInfo = null;
let gameInfo;
if(this.state.gameInfo.turn === 0 && this.state.gameInfo.round !== 0){
let prevRound = this.gameStateHistory[this.state.gameInfo.round-1];
gameInfo = deepCopy(prevRound[prevRound.length-1]);