created basic game w/ table and animations
@@ -17,6 +17,6 @@ This is a Symfony 3 project w/ React JS in standalone mode and w/ WebSocket.
|
|||||||
|
|
||||||
$ nohup bin/console gos:websocket:server &
|
$ nohup bin/console gos:websocket:server &
|
||||||
|
|
||||||
2.) React JS WebPack watch generator w/ babel presets: es2015
|
2.) React JS WebPack watch generator w/ babel presets: es2015, react
|
||||||
|
|
||||||
$ webpack --progress --colors --watch
|
$ webpack --progress --colors --watch
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@import 'https://fonts.googleapis.com/css?family=Open+Sans:700';
|
||||||
|
|
||||||
#mine-wrapper,
|
#mine-wrapper,
|
||||||
#mine-wrapper * {
|
#mine-wrapper * {
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
@@ -7,26 +9,228 @@
|
|||||||
#mine-wrapper .grid {
|
#mine-wrapper .grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
|
||||||
width: 642px;
|
width: 642px;
|
||||||
border: 1px solid red;
|
border: 1px solid #51c2fe;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
cursor: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mine-wrapper .grid .field {
|
#mine-wrapper .grid .field-wrapper {
|
||||||
background: #fff;
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-target {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
width: 45px;
|
||||||
|
top: -2.5px;
|
||||||
|
left: -2.5px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper:hover > img.field-target {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-blue-last,
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-red-last {
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-blue-last.last-clicked,
|
||||||
|
#mine-wrapper .grid .field-wrapper > img.field-red-last.last-clicked {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field {
|
||||||
|
background: #61defa;
|
||||||
|
background: -moz-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: linear-gradient(to right, #61defa 0%, #119dec 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#61defa', endColorstr='#119dec', GradientType=1);
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border: 1px solid red;
|
border: 2px solid #51c2fe;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 35px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mine-wrapper .grid .field.active {
|
#mine-wrapper .grid .field-wrapper .field .field-corner {
|
||||||
background: blue;
|
background: url('/bundles/mineseeker/images/bg-corner-outbg.png') no-repeat top left;
|
||||||
color: #FFFFFF;
|
background-size: 100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mine-wrapper .grid .field.active.mine {
|
#mine-wrapper .grid .field-wrapper .field.active {
|
||||||
background: red;
|
background: #fde717;
|
||||||
color: #FFFFFF;
|
background: -moz-linear-gradient(left, #fde717 0%, #f5b807 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #fde717 0%, #f5b807 100%);
|
||||||
|
background: linear-gradient(to right, #fde717 0%, #f5b807 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fde717', endColorstr='#f5b807', GradientType=1);
|
||||||
|
border: 2px solid #f6d762;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-number {
|
||||||
|
-webkit-animation: bubbleNumber 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
animation: bubbleNumber 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bubbleNumber {
|
||||||
|
0% {
|
||||||
|
background: #61defa;
|
||||||
|
background: -moz-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: linear-gradient(to right, #61defa 0%, #119dec 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#61defa', endColorstr='#119dec', GradientType=1);
|
||||||
|
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-border-radius: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-mine {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-mine > img {
|
||||||
|
width: 65%;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
-ms-transform: rotate(7deg); /* IE 9 */
|
||||||
|
-webkit-transform: rotate(7deg); /* Chrome, Safari, Opera */
|
||||||
|
transform: rotate(7deg);
|
||||||
|
-webkit-animation: mineFlagLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
animation: mineFlagLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mineFlagLoad {
|
||||||
|
0% {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: 15px;
|
||||||
|
|
||||||
|
-ms-transform: rotate(9deg); /* IE 9 */
|
||||||
|
-webkit-transform: rotate(9deg); /* Chrome, Safari, Opera */
|
||||||
|
transform: rotate(9deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
margin-bottom: -5px;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
margin-bottom: 3px;
|
||||||
|
margin-left: 0;
|
||||||
|
|
||||||
|
-ms-transform: rotate(-9deg); /* IE 9 */
|
||||||
|
-webkit-transform: rotate(-9deg); /* Chrome, Safari, Opera */
|
||||||
|
transform: rotate(-9deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active .flag-mine .flag-mine-base {
|
||||||
|
position: absolute;
|
||||||
|
background: #000000;
|
||||||
|
width: 25px;
|
||||||
|
height: 22px;
|
||||||
|
bottom: -12px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -10.5px;
|
||||||
|
|
||||||
|
-webkit-border-radius: 50%;
|
||||||
|
border-radius: 50%;
|
||||||
|
-webkit-animation: mineBaseLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
animation: mineBaseLoad 500ms cubic-bezier(.36, .07, .19, .97) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mineBaseLoad {
|
||||||
|
0% {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
margin-bottom: -5px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*#mine-wrapper .grid .field-wrapper .field.active .field-corner {*/
|
||||||
|
/*-webkit-transform: scale(1);*/
|
||||||
|
/*transform: scale(1);*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.mine {
|
||||||
|
background: #61defa;
|
||||||
|
background: -moz-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: -webkit-linear-gradient(left, #61defa 0%, #119dec 100%);
|
||||||
|
background: linear-gradient(to right, #61defa 0%, #119dec 100%);
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#61defa', endColorstr='#119dec', GradientType=1);
|
||||||
|
border: 2px solid #51c2fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-1 {
|
||||||
|
color: #0000ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-2 {
|
||||||
|
color: #079433;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-3 {
|
||||||
|
color: #fd1400;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-4 {
|
||||||
|
color: #0c099e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-5 {
|
||||||
|
color: #7b4c01;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-6 {
|
||||||
|
color: #008388;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-7 {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field.active.color-8 {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mine-wrapper .grid .field-wrapper .field img {
|
||||||
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 36 KiB |
BIN
src/Mine/SeekerBundle/Resources/public/images/bg-bomb-outbg.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
@@ -11,7 +11,11 @@ class GridControl extends React.Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
grid: grid.state.grid,
|
grid: grid.state.grid,
|
||||||
updated: []
|
updated: [],
|
||||||
|
lastClicked: {
|
||||||
|
red: null,
|
||||||
|
blue: null
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +32,13 @@ class GridControl extends React.Component {
|
|||||||
var currentField = this.refs[this.refString(row, col)];
|
var currentField = this.refs[this.refString(row, col)];
|
||||||
|
|
||||||
currentField.setState({
|
currentField.setState({
|
||||||
|
currentImage: this.state.grid[row][col],
|
||||||
currentObj: this.state.grid[row][col],
|
currentObj: this.state.grid[row][col],
|
||||||
active: true
|
active: true
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ez azért kell, mert amíg nem fut le a showAppropriateFields(), addig nem updatelődik a GridField.state
|
* TODO ez azért kell, mert amíg nem fut le a showAppropriateFields(), addig nem updatelődik a GridField.state
|
||||||
*/
|
*/
|
||||||
if (
|
if (
|
||||||
this.state.grid[row][col] !== 0 &&
|
this.state.grid[row][col] !== 0 &&
|
||||||
@@ -99,17 +104,56 @@ class GridControl extends React.Component {
|
|||||||
/**
|
/**
|
||||||
* Player control method
|
* Player control method
|
||||||
* @param currentObject
|
* @param currentObject
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
*/
|
*/
|
||||||
handlePlayers(currentObject) {
|
handleGridField(currentObject, x, y) {
|
||||||
var userControl = this.refs.userControl,
|
var userControl = this.refs.userControl,
|
||||||
|
gridFieldControl = this.refs[this.refString(x, y)],
|
||||||
activePlayer = userControl.state.activePlayer ? 'blue' : 'red';
|
activePlayer = userControl.state.activePlayer ? 'blue' : 'red';
|
||||||
|
|
||||||
|
/** update last cliked grid field */
|
||||||
|
if (this.state.lastClicked[activePlayer] !== null) {
|
||||||
|
this.refs[this.refString(this.state.lastClicked[activePlayer][0], this.state.lastClicked[activePlayer][1])].setState({
|
||||||
|
lastClickedRed: false,
|
||||||
|
lastClickedBlue: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gridFieldControl.state.active) {
|
||||||
|
this.state.lastClicked[activePlayer] = [x, y];
|
||||||
|
|
||||||
|
/** If you found mine */
|
||||||
if (currentObject === 'm') {
|
if (currentObject === 'm') {
|
||||||
|
userControl.setState({
|
||||||
|
mines: userControl.state.mines - 1
|
||||||
|
});
|
||||||
|
|
||||||
userControl.refs[activePlayer].setState({
|
userControl.refs[activePlayer].setState({
|
||||||
mines: userControl.refs[activePlayer].state.mines + 1
|
mines: userControl.refs[activePlayer].state.mines + 1
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.state.lastClicked[activePlayer] = [x, y];
|
||||||
|
|
||||||
|
gridFieldControl.setState({
|
||||||
|
currentImage: gridFieldControl.state.icons.root + gridFieldControl.state.icons.flag[activePlayer]
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
userControl.state.activePlayer = userControl.state.activePlayer ? 0 : 1;
|
userControl.state.activePlayer = userControl.state.activePlayer ? 0 : 1;
|
||||||
|
|
||||||
|
/** It is a number */
|
||||||
|
if (!isNaN(currentObject)) {
|
||||||
|
gridFieldControl.setState({
|
||||||
|
currentImage: currentObject
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** set-up last clicked */
|
||||||
|
gridFieldControl.setState({
|
||||||
|
lastClickedRed: activePlayer === 'red',
|
||||||
|
lastClickedBlue: activePlayer === 'blue'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +164,7 @@ class GridControl extends React.Component {
|
|||||||
onClick(coords) {
|
onClick(coords) {
|
||||||
var currentField = this.refs[this.refString(coords[0], coords[1])];
|
var currentField = this.refs[this.refString(coords[0], coords[1])];
|
||||||
this.showAppropriateFields(currentField, coords[0], coords[1]);
|
this.showAppropriateFields(currentField, coords[0], coords[1]);
|
||||||
this.handlePlayers(this.state.grid[coords[0]][coords[1]]);
|
this.handleGridField(this.state.grid[coords[0]][coords[1]], coords[0], coords[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGrid() {
|
renderGrid() {
|
||||||
@@ -131,7 +175,6 @@ class GridControl extends React.Component {
|
|||||||
grid.push(
|
grid.push(
|
||||||
<GridField row={i}
|
<GridField row={i}
|
||||||
col={k}
|
col={k}
|
||||||
obj={this.state.grid[i][k]}
|
|
||||||
ref={this.refString(i, k)}
|
ref={this.refString(i, k)}
|
||||||
key={i + k * Math.random() * 0.5}
|
key={i + k * Math.random() * 0.5}
|
||||||
onClick={this.onClick.bind(this, [i, k])}
|
onClick={this.onClick.bind(this, [i, k])}
|
||||||
|
|||||||
@@ -6,21 +6,71 @@ class GridField extends React.Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
currentObj: 'w',
|
currentObj: 'w',
|
||||||
obj: this.props.obj,
|
currentImage: null,
|
||||||
active: false
|
active: false,
|
||||||
|
lastClickedRed: false,
|
||||||
|
lastClickedBlue: false,
|
||||||
|
icons: {
|
||||||
|
root: 'bundles/mineseeker/images/',
|
||||||
|
water: {
|
||||||
|
1: 'bg-wave-1-outbg.png',
|
||||||
|
2: 'bg-wave-1-outbg.png',
|
||||||
|
3: 'bg-wave-2-outbg.png'
|
||||||
|
},
|
||||||
|
flag: {
|
||||||
|
red: 'bg-flag-red-outbg.png',
|
||||||
|
blue: 'bg-flag-blue-outbg.png'
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
lastBlue: 'bg-last-blue-outbg.png',
|
||||||
|
lastRed: 'bg-last-red-outbg.png',
|
||||||
|
crosshair: 'bg-target-outbg.png',
|
||||||
|
crosshairBomb: 'bg-target-bomb-outbg.png'
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
isActive() {
|
componentWillMount() {
|
||||||
|
var wave = Math.floor(Math.random() * 3) + 1;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
currentImage: this.state.icons.root + this.state.icons.water[wave]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
classNameWhenActive() {
|
||||||
return 'field'
|
return 'field'
|
||||||
+ (this.state.active === true ? ' active' : '')
|
+ (this.state.active === true ? ' active' : '')
|
||||||
+ (this.state.active === true && this.state.obj === 'm' ? ' mine' : '');
|
+ (this.state.active === true && this.state.currentObj === 'm' ? ' mine' : '')
|
||||||
|
+ ' color-' + this.state.currentObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentImage() {
|
||||||
|
return isNaN(this.state.currentImage)
|
||||||
|
? <div className="flag-mine"><img src={this.state.currentImage}/><div className="flag-mine-base"></div></div>
|
||||||
|
: this.state.currentImage ? <div className="flag-number">{this.state.currentImage}</div> : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
lastClickedRed() {
|
||||||
|
return 'field-red-last' + (this.state.lastClickedRed ? ' last-clicked' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
lastClickedBlue() {
|
||||||
|
return 'field-blue-last' + (this.state.lastClickedBlue ? ' last-clicked' : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className={this.isActive()} onClick={this.props.onClick}>
|
<div className="field-wrapper">
|
||||||
{this.state.currentObj}
|
<img className="field-target" src="/bundles/mineseeker/images/bg-target-outbg.png" alt="target" onClick={this.props.onClick}/>
|
||||||
|
<img className={this.lastClickedRed()} src="/bundles/mineseeker/images/bg-last-red-outbg.png" alt="red last"/>
|
||||||
|
<img className={this.lastClickedBlue()} src="/bundles/mineseeker/images/bg-last-blue-outbg.png" alt="blue last"/>
|
||||||
|
<div className={this.classNameWhenActive()}>
|
||||||
|
<div className="field-corner">
|
||||||
|
{this.currentImage()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class UserControl extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<User ref="blue" name={this.props.blue}/>
|
<User ref="blue" name={this.props.blue}/>
|
||||||
|
<div>Active mines: {this.state.mines} pcs</div>
|
||||||
<User ref="red" name={this.props.red}/>
|
<User ref="red" name={this.props.red}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||