import React from 'react'; import Grid from './grid/grid'; import GridControl from './grid/grid-control'; class MineSeeker extends React.Component { constructor(props) { super(props); let gameAssoc = props.gameId !== '' ? props.gameId : this.makeGameAssoc(50); let channel = "mineseeker/channel/" + gameAssoc; this.state = { env: props.env, ssl: props.ssl, gameInherited: props.gameId !== '', gameAssoc: gameAssoc, channel: channel, session: null, createGrid: false, stepCache: [], connectionLost: false, end: false } } makeGameAssoc(len) { let text = ""; let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (let i = 0; i < len; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } currectGridSize() { let $field = $('#mine-wrapper .grid'); $field.height($field.width()); $field = $('#mine-wrapper .grid .field-wrapper'); $field.height($field.width()); $('#mine-wrapper .grid .field-wrapper .field') .height($field.width()) .css('line-height', ($field.width() - 2) + 'px'); } /** * STEP * * @param coords * @returns {{red: *, blue: *}} */ makePointsCalcAndStep(coords) { let users = this.refs.gridControl.refs.userControl, activePlayer = users.state.activePlayer ? 'blue' : 'red', inactivePlayer = users.state.activePlayer ? 'red' : 'blue', redPoints = activePlayer === 'red' ? users.refs[activePlayer].state.mines : users.refs[inactivePlayer].state.mines, bluePoints = activePlayer === 'blue' ? users.refs[activePlayer].state.mines : users.refs[inactivePlayer].state.mines; this.refs.gridControl.stepEvent(coords); let mineCache = this.refs.gridControl.state.foundUserMineCache; redPoints += activePlayer === 'red' ? mineCache : 0; bluePoints += activePlayer === 'blue' ? mineCache : 0; return {red: redPoints, blue: bluePoints}; } /** * START * * @param payload */ makeGameStart(payload) { /** every time the blue starts */ this.refs.gridControl.refs.userControl.setState({activePlayer: 1}); /** Set up player names w/ server data */ this.refs.gridControl.refs.userControl.refs.red.setState({ name: payload.users.red !== '' ? payload.users.red : payload.users.redAnon, }); this.refs.gridControl.refs.userControl.refs.blue.setState({ name: payload.users.blue !== '' ? payload.users.blue : payload.users.blueAnon, desc: this.refs.gridControl.state.webPlayer === 'blue' ? this.refs.gridControl.state.desc.you : this.refs.gridControl.state.desc.buddy, active: true, }); this.refs.gridControl.setState({overlay: false}); } /** * THE END * * @param bluePoints * @param redPoints * @param resign */ makeGameEndIfItEnds(bluePoints, redPoints, resign = false) { let redWins = redPoints > 25, blueWins = bluePoints > 25; if (redWins || blueWins || resign) { this.refs.gridControl.state.sound.won.play(); if (false === resign) { this.refs.gridControl.setState({ overlay: true, overlayTitle: (redWins ? 'Red' : 'Blue') + " wins the game!", overlaySubTitle: "Play again!" }); } this.refs.gridControl.showLeftMines(); this.refs.gridControl.refs.userControl.setState({activePlayer: false}); this.refs.gridControl.refs.userControl.refs.red.setState({desc: ""}); this.refs.gridControl.refs.userControl.refs.blue.setState({desc: ""}); } } resignProcess(color) { this.refs.gridControl.setState({ overlay: true, overlayTitle: color === this.refs.gridControl.state.webPlayer ? "You have been give up" : "Your opponent has been resigned", overlaySubTitle: color === this.refs.gridControl.state.webPlayer ? "You LOSE!" : "You WIN!" }); this.setState({end: true}); this.makeGameEndIfItEnds(0, 0, true); } clickResign() { /** PUBLISH */ this.state.session.publish(this.state.channel, { 'resign': this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red' }); this.resignProcess(this.refs.gridControl.state.webPlayer); } clickResignCancel() { this.refs.gridControl.setState({ overlay: false }); } /** RESIGN */ resign() { let users = this.refs.gridControl.refs.userControl, activePlayer = users.state.activePlayer ? 'blue' : 'red'; if (this.refs.gridControl.state.webPlayer === activePlayer) { this.refs.gridControl.setState({ overlay: true, overlayTitle: "Are u sure u want to resign?!", overlaySubTitle:
Yes No!
}); } } /** * @see https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus * @see https://developers.facebook.com/docs/sharing/reference/send-dialog * @see https://developers.facebook.com/docs/plugins/share-button/ */ clickFBShare() { let display = 'popup'; FB.getLoginStatus(function (response) { display = response.status === 'connected' ? 'dialog' : 'popup'; }); FB.ui({ method: 'send', display: display, link: window.location.href + '/' + this.state.gameAssoc, }); } wInit(session, gridServer, gridClient) { this.setState({session: session}); /** save session to GridControl */ /** render grid fields - @see #12 */ this.refs.gridControl.setState({ grid: this.state.gameInherited ? gridServer : gridClient, channel: this.state.channel, desc: { buddy:
Your buddy is
making a
move.
, you:
It is your turn!
Make a move.
}, overlay: true, overlayTitle: "We are waiting for your opponent...", overlaySubTitle: this.state.gameAssoc ?

Share this unique link w/ your opponent

{this.state.env !== 'dev' && Share in Facebook message } {this.state.env === 'dev' && Play w/ me! }
: '', renderGridFields: this.state.gameAssoc }); } wSubscribe(payload, rpcUsers = null) { this.state.env === 'dev' && console.info( (typeof payload.user !== 'undefined' ? payload.user : 'user') + " has been subscribed to the channel!" ); let firstUser = !rpcUsers; this.refs.gridControl.state.webPlayer === null && this.refs.gridControl.setState({ webPlayer: payload.user === payload.users.blue || ( firstUser && payload.users.blueAnon !== '' || !firstUser && (rpcUsers.blueAnon === '' && rpcUsers.blue === '') ) ? 'blue' : 'red' }); /** rwd */ (900 > $(document).width()) && this.currectGridSize(); /** every user has been came */ if ( payload.userCnt === 2 && ( !this.state.connectionLost || this.state.connectionLost && false === this.refs.gridControl.refs.userControl.state.activePlayer && !this.state.end ) ) { this.makeGameStart(payload); } } wUnsubscribe(payload) { this.state.env === 'dev' && console.info(payload.msg); this.refs.gridControl.setState({ overlay: true, overlayTitle: "The connection has been lost w/ your friend...", overlaySubTitle: "Please, restart the game!" }); } wTopic(payload) { /** Auto-Step if this player is not the current user */ if (this.refs.gridControl.state.webPlayer !== payload.data.player) { if (null === payload.data.resign) { this.state.env === 'dev' && console.warn(payload.user + " has been stepped to coords: " + payload.data.coords[0] + ', ' + payload.data.coords[1]); this.state.env === 'dev' && console.warn('Opponent stepped: Auto-Step process'); this.refs.gridControl.refs.userControl.setState({bombSelected: payload.data.bomb}); /** STEP */ let points = this.makePointsCalcAndStep(payload.data.coords); /** THE END */ this.makeGameEndIfItEnds(points.blue, points.red); } else { /** RESIGN */ /** THE END */ this.resignProcess(payload.data.resign); } } } /** Connect - Subscribe */ subscribe(rpcUsers = null) { this.state.session.subscribe( this.state.channel, (uri, payload, log) => { let isTopicEvent = typeof payload.data !== 'undefined', isNotUnsubscribe = typeof payload.msg === 'undefined'; /** CONNECTION */ if (isTopicEvent) { this.wTopic(payload); } else { if (isNotUnsubscribe) { this.wSubscribe(payload, rpcUsers); } else { this.wUnsubscribe(payload); } } /** RECONNECTION */ if (payload.userCnt === 2 && this.state.connectionLost) { this.state.env === 'dev' && console.info('Reconnection process'); /** PUBLISH */ let cache = this.state.stepCache; cache.forEach((item) => this.state.session.publish(this.state.channel, item)); this.setState({connectionLost: false, stepCache: []}); } }); } connectWithWebsocket() { /** Create Websocket w/ Bahnhof.js */ let websocket = WS.connect( // this.state.env === 'dev' // ? "ws://localhost:6450" (this.state.ssl === 'true' ? "wss" : "ws") + "://" + window.location.hostname + ":6450/" // : (this.state.ssl === 'true' ? "wss" : "ws") + "://mineseeker.splendidbear.org:6450" ); /** * Connect * Session is an Autobahn JS WAMP session. */ websocket.on("socket/connect", (session) => { this.state.env === 'dev' && console.info("Successfully connected to the Server!"); if (!this.state.connectionLost) { let gridClient = this.state.gameInherited || new Grid().state.grid; /** * Connect - RPC * Send grid information to the server */ session .call( this.state.gameInherited ? "mineseeker-rpc/connectGame" : "mineseeker-rpc/startGame", this.state.gameInherited ? this.state.gameAssoc : [window.btoa(JSON.stringify(gridClient)), this.state.gameAssoc] ) .then( (data) => { this.state.env === 'dev' && console.info('RPC has been called'); let serverData = data[0] !== true ? JSON.parse(window.atob(data)) : data; /** Check the grid if the user is inherited @see #30 */ if ((this.state.gameInherited && typeof serverData.grid !== 'undefined') || !this.state.gameInherited) { this.wInit(session, serverData.grid, gridClient); this.subscribe(this.state.gameInherited && serverData.users); } else { this.refs.gridControl.setState({ overlay: true, overlayTitle: "This channel does not exists!", overlaySubTitle: Restart game! }); console.error("This channel does not exists!"); } }, (error, desc) => this.state.env === 'dev' && console.error(["RPC Error", error, desc]) ); } else { this.setState({session: session}); this.subscribe(); } }); /** * DisConnect * Error provides us with some insight into the disconnection: error.reason and error.code */ websocket.on("socket/disconnect", (error) => { this.state.env === 'dev' && console.error("Disconnected for " + error.reason + " with code " + error.code); error.code === 6 && this.setState({connectionLost: true}); error.code === 3 && setTimeout(this.componentDidMount().bind(this), 500); }); } /** After rendering */ componentDidMount() { this.connectWithWebsocket(); } /** * Cache the steps unless reconnection * * @param dataPack */ cachePublish(dataPack) { let cache = this.state.stepCache; cache.push(dataPack); this.setState({stepCache: cache}); } onClick(coords) { let activePlayer = this.refs.gridControl.refs.userControl.state.activePlayer ? 'blue' : 'red'; /** if the clicked field is NEVER CLICKED */ if (this.refs.gridControl.checkFieldHasBeenNeverClicked(coords[0], coords[1])) { /** Player step and it is the current player */ if (activePlayer === this.refs.gridControl.state.webPlayer) { /** STEP */ let points = this.makePointsCalcAndStep(coords); /** THE END */ this.makeGameEndIfItEnds(points.blue, points.red); let dataPack = { 'coords': coords, 'player': activePlayer, 'bomb': this.refs.gridControl.refs.userControl.state.bombSelected, 'redPoints': points.red, 'bluePoints': points.blue, 'resign': null, 'redExplodedBomb': activePlayer === 'red' && this.refs.gridControl.refs.userControl.state.bombSelected, 'blueExplodedBomb': activePlayer === 'blue' && this.refs.gridControl.refs.userControl.state.bombSelected }; /** PUBLISH */ !this.state.connectionLost ? this.state.session.publish(this.state.channel, dataPack) : this.cachePublish(dataPack); } } } render() { return ( ); } } export default MineSeeker;