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 &
|
||||
|
||||
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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@import 'https://fonts.googleapis.com/css?family=Open+Sans:700';
|
||||
|
||||
#mine-wrapper,
|
||||
#mine-wrapper * {
|
||||
-webkit-box-sizing: border-box;
|
||||
@@ -7,26 +9,228 @@
|
||||
#mine-wrapper .grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
width: 642px;
|
||||
border: 1px solid red;
|
||||
border: 1px solid #51c2fe;
|
||||
margin: 0 auto;
|
||||
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
#mine-wrapper .grid .field {
|
||||
background: #fff;
|
||||
#mine-wrapper .grid .field-wrapper {
|
||||
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;
|
||||
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 {
|
||||
background: blue;
|
||||
color: #FFFFFF;
|
||||
#mine-wrapper .grid .field-wrapper .field .field-corner {
|
||||
background: url('/bundles/mineseeker/images/bg-corner-outbg.png') no-repeat top left;
|
||||
background-size: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#mine-wrapper .grid .field.active.mine {
|
||||
background: red;
|
||||
color: #FFFFFF;
|
||||
#mine-wrapper .grid .field-wrapper .field.active {
|
||||
background: #fde717;
|
||||
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 = {
|
||||
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)];
|
||||
|
||||
currentField.setState({
|
||||
currentImage: this.state.grid[row][col],
|
||||
currentObj: this.state.grid[row][col],
|
||||
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 (
|
||||
this.state.grid[row][col] !== 0 &&
|
||||
@@ -99,17 +104,56 @@ class GridControl extends React.Component {
|
||||
/**
|
||||
* Player control method
|
||||
* @param currentObject
|
||||
* @param x
|
||||
* @param y
|
||||
*/
|
||||
handlePlayers(currentObject) {
|
||||
handleGridField(currentObject, x, y) {
|
||||
var userControl = this.refs.userControl,
|
||||
gridFieldControl = this.refs[this.refString(x, y)],
|
||||
activePlayer = userControl.state.activePlayer ? 'blue' : 'red';
|
||||
|
||||
if (currentObject === 'm') {
|
||||
userControl.refs[activePlayer].setState({
|
||||
mines: userControl.refs[activePlayer].state.mines + 1
|
||||
/** 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') {
|
||||
userControl.setState({
|
||||
mines: userControl.state.mines - 1
|
||||
});
|
||||
|
||||
userControl.refs[activePlayer].setState({
|
||||
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 {
|
||||
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'
|
||||
});
|
||||
} else {
|
||||
userControl.state.activePlayer = userControl.state.activePlayer ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +164,7 @@ class GridControl extends React.Component {
|
||||
onClick(coords) {
|
||||
var currentField = this.refs[this.refString(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() {
|
||||
@@ -131,7 +175,6 @@ class GridControl extends React.Component {
|
||||
grid.push(
|
||||
<GridField row={i}
|
||||
col={k}
|
||||
obj={this.state.grid[i][k]}
|
||||
ref={this.refString(i, k)}
|
||||
key={i + k * Math.random() * 0.5}
|
||||
onClick={this.onClick.bind(this, [i, k])}
|
||||
|
||||
@@ -6,21 +6,71 @@ class GridField extends React.Component {
|
||||
|
||||
this.state = {
|
||||
currentObj: 'w',
|
||||
obj: this.props.obj,
|
||||
active: false
|
||||
currentImage: null,
|
||||
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'
|
||||
+ (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() {
|
||||
return (
|
||||
<div className={this.isActive()} onClick={this.props.onClick}>
|
||||
{this.state.currentObj}
|
||||
<div className="field-wrapper">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ class UserControl extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<User ref="blue" name={this.props.blue}/>
|
||||
<div>Active mines: {this.state.mines} pcs</div>
|
||||
<User ref="red" name={this.props.red}/>
|
||||
</div>
|
||||
);
|
||||
|
||||