diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 44dd219..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/README.md b/README.md index e661ac5..fb1537f 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,21 @@ This is 3D multiplayer version of the classic Tron game where players compete to be the last rider standing. ## Live Game -To play a live version of this game please click here. +To play a live version of this game please click here http://3d-tron.com. ## Game Play Design -3D Tron is a fan variant of the classic Tron game, the player guides a Light Cycle in an arena against opponents, while avoiding the walls and trails of light left behind by all Light Cycles. The player must maneuver quickly and precisely in order to force opponents to run into walls or trails of light. +3D Tron is a fan variant of the classic Tron game, where players guide a Light Cycle in an arena against opponents, while avoiding the walls and trails of light left behind by all Light Cycles. The players must maneuver quickly and precisely in order to force opponents to run into walls or trails of light. ## Controls -User arrow keys to turn -Avoid the walls and trails of light left behind by all Light Cycles +User arrow or "w a s d" keys to turn. +Avoid the walls and trails of light left behind by other players. ## Architecture -3D Tron is built on Node.js using Socket.IO for client-server interaction, Three.js and Whitestorm.js for 3D graphics rendering, Physi.js for the physics engine, React for HTML rendering, and Redux for both client and server app state and game state management. +3D Tron is built on Node.js using Socket.io for client-server interaction, Three.js and Whitestorm.js for 3D graphics rendering, Physi.js for the physics engine, React for HTML rendering, and Redux for both client and server app state and game state management. Handling of the game logic is distributed between the client and the server. Clients run their own physics calculations to compute their next position and orientation, while the server manages and modifies the master game state according to game logic and client events such as collisions with objects or other players. + +## Set up on local machine +- Clone the repository +- npm install +- npm start (Runs webpack and starts the server on port 3000) diff --git a/browser/components/App.js b/browser/components/App.js index 7b736c1..6b237f7 100644 --- a/browser/components/App.js +++ b/browser/components/App.js @@ -1,28 +1,20 @@ -import React, { Component } from 'react'; +'use strict'; +import React from 'react'; import { connect } from 'react-redux'; import Game from './Game'; import Landing from './Landing'; import LobbyRoom from './LobbyRoom'; -const App = ({ gameState }) => { - -return ( -
- {gameState.isEnter && !gameState.isPlaying && } - {!gameState.isEnter && !gameState.isPlaying && } - {gameState.isPlaying && } -
+const App = ({ gameState }) => ( +
+ { gameState.isEnter && !gameState.isPlaying && } + { !gameState.isEnter && !gameState.isPlaying && } + { gameState.isPlaying && } +
); -}; -// if(this.props.gameState === 'landing') render Landing -// if asdfasd = playing || dead render game - const mapStateToProps = ({ gameState }) => ({ gameState }); const mapDispatchToProps = null; -export default connect( - mapStateToProps, - mapDispatchToProps -)(App); +export default connect(mapStateToProps, mapDispatchToProps)(App); diff --git a/browser/components/Chat.js b/browser/components/Chat.js index d63782b..2ba3341 100644 --- a/browser/components/Chat.js +++ b/browser/components/Chat.js @@ -1,9 +1,7 @@ +'use strict'; import React, { Component } from 'react'; import { connect } from 'react-redux'; import socket from '../socket'; -import { startChat, stopChat } from '../reducers/gameState'; - - class Chat extends Component { constructor(props){ @@ -16,7 +14,7 @@ class Chat extends Component { } componentWillUpdate(){ - this.refs.messageBox.scrollTop = this.refs.messageBox.scrollHeight; + this.refs.messageBox.scrollTop = this.refs.messageBox.scrollHeight; } @@ -35,38 +33,34 @@ class Chat extends Component { render() { return (
-
- -
- { if (evt.key === 'Enter') this.sendMessage(); }} - maxLength={70} - type="text" - id="chat-bar" - placeholder="press 'enter' to send"/> +
+ +
+ { if (evt.key === 'Enter') this.sendMessage(); }} + maxLength={70} + type="text" + id="chat-bar" + placeholder="press 'enter' to send" />
- ); + ); } } const mapStateToProps = ({ messages, gameState }) => ({ messages, gameState }); +const mapDispatchToProps = null; -const mapDispatchToProps = dispatch => ({ -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Chat); +export default connect(mapStateToProps, mapDispatchToProps)(Chat); diff --git a/browser/components/Game.js b/browser/components/Game.js index 80a4408..eb2d180 100644 --- a/browser/components/Game.js +++ b/browser/components/Game.js @@ -1,76 +1,57 @@ +'use strict'; import React, { Component } from 'react'; import { connect } from 'react-redux'; -import math from 'mathjs'; -import world, { speed } from '../game/world'; -import { turnPlayer } from '../reducers/mainPlayer'; -import store from '../store'; +import world from '../game/world'; import socket from '../socket'; -import { cameraSetOnStart } from '../game/gamePlayFunctions' -import { DeadNoWinner, Winner, DeadWithWinner} from './InGame' -console.log("SOCKET ID LOCAL STORAGE (IN THE FRONT END)", localStorage.getItem('mySocketId')); +import { turnPlayer } from '../game/directionsFunctions'; +import { cameraSetOnStart } from '../game/gamePlayFunctions'; +import { DeadNoWinner, Winner, DeadWithWinner } from './InGame'; class Game extends Component { - constructor(props) { - super(props); - } componentDidMount() { - console.log("CDM PROPS", this.props) const players = this.props.players; + const myPlayer = this.props.mainPlayer; world.start(); + myPlayer.ball.add(world.camera); + cameraSetOnStart(myPlayer); players.forEach(player => { - player.ball.native.addEventListener('collision', (collidedWith) => { - console.log("player", player) - console.log("collidedWith", collidedWith) - socket.emit('ball-collision', {signature: player.signature, id: player.id}); - }, true); player.si = setInterval(player.tail, 10); }); - cameraSetOnStart(this.props.mainPlayer) - } - - render(){ - const TURN_AUDIO = document.createElement('audio'); - TURN_AUDIO.src = 'mp3/shortBikeTurn.m4a'; - TURN_AUDIO.load(); - if (this.props.mainPlayer) { - const player = this.props.mainPlayer; - player.ball.add(world.camera); + myPlayer.ball.native.addEventListener('collision', (collidedWith) => { + // console.log("collidedWith", collidedWith); + socket.emit('ball-collision', {signature: myPlayer.signature, id: myPlayer.id}); + }); - document.addEventListener('keydown', (event) => { - const validKeys = [37, 39, 38, 40, 87, 65, 83, 68]; - if (validKeys.includes(event.keyCode)) { - store.dispatch(turnPlayer(event.keyCode)); - TURN_AUDIO.play(); - } - }); + document.addEventListener('keydown', (event) => { + const TURN_AUDIO = document.createElement('audio'); + TURN_AUDIO.src = 'mp3/shortBikeTurn.m4a'; + TURN_AUDIO.load(); + const validKeys = [37, 39, 38, 40, 87, 65, 83, 68]; + if (validKeys.includes(event.keyCode)) { + turnPlayer(event.keyCode, myPlayer); + TURN_AUDIO.play(); + } + }); + } - document.addEventListener('keyup', (event) => { - const validKeys = [37, 39, 38, 40, 87, 65, 83, 68]; - if (validKeys.includes(event.keyCode)) { - TURN_AUDIO.stop(); - } - }); + render() { return (
- { + { this.props.mainPlayer.status === 'dead' && this.props.players.filter(player => player.winner === true).length === 0 ? : null } - { - this.props.mainPlayer.status === 'dead' && !this.props.mainPlayer.winner && this.props.players.filter(player => player.winner === true).length === 1 ? : null + { + this.props.mainPlayer.status === 'dead' && !this.props.mainPlayer.winner && this.props.players.filter(player => player.winner).length === 1 ? : null } - { - this.props.mainPlayer.winner === true ? : null + + { + this.props.mainPlayer.winner ? : null }
- - ); - } else { - return null - } - + ); } } diff --git a/browser/components/InGame.js b/browser/components/InGame.js index aa66702..026f7bf 100644 --- a/browser/components/InGame.js +++ b/browser/components/InGame.js @@ -1,51 +1,35 @@ -import React, { Component } from 'react'; +'use strict'; +import React from 'react'; -export const DeadNoWinner = () => { - return( -
-
-
You Crashed!
-
-
- Click and drag with your mouse to watch the rest of the game! -
-
- ) -} +export const DeadNoWinner = () => ( +
+
+
You Crashed!
+
+
+ Click and drag with your mouse to watch the rest of the game! +
+
+); -export const Winner = () => { - return( -
-
-
You Win!
-
-
- Game will reload home page soon -
-
- ) -} +export const Winner = () => ( +
+
+
You Win!
+
+
+ Game will reload home page soon +
+
+); -class dWW extends Component { - constructor(props){ - super(props) - } - render(){ - let winner = this.props.players.filter(player => player.winner === true)[0].playerName - return( -
-
-
{winner} Wins!
-
-
- Game will reload home page soon -
-
- ) - - } -} - -import { connect } from 'react-redux' - -export const DeadWithWinner = connect(({ players }) => ({ players }))(dWW) \ No newline at end of file +export const DeadWithWinner = ({ players }) => ( +
+
+
{players.find(player => player.winner).playerName} Wins!
+
+
+ Game will reload home page soon +
+
+); diff --git a/browser/components/Landing.js b/browser/components/Landing.js index 317a15c..23aeb60 100644 --- a/browser/components/Landing.js +++ b/browser/components/Landing.js @@ -1,95 +1,52 @@ -import React, { Component } from 'react'; +'use strict'; +import React from 'react'; import { connect } from 'react-redux'; import socket from '../socket'; -import { addPlayerName } from '../reducers/players'; import { enterLobby } from '../reducers/gameState'; import { toggleSong } from '../reducers/musicPlayer'; -import world from '../game/world'; -import store from '../store'; -//not needed yet -// import ControlPanel from './ControlPanel'; -// import BugReportForm from './BugReportForm'; - -class Landing extends Component { - constructor(props) { - super(props); - // this.readyPlayerEmitter = this.readyPlayerEmitter.bind(this); - } - - - render() { - - function playerNameEmitter(event) { +const Landing = ({ musicPlayer, playerNameEmitter, toggleSong }) => ( +
+
3D TRON
+
+
+ + +
+ +
+
Use "w a s d" or arrow keys to turn
+
+); + +const mapStateToProps = ({ musicPlayer }) => ({ musicPlayer }); +const mapDispatchToProps = dispatch => ({ + playerNameEmitter: (event) => { event.preventDefault(); - console.log("GETTING HERE?"); const socketId = localStorage.getItem('mySocketId'); const playerName = event.target.nickName.value; socket.emit('playerName', socketId, playerName); - store.dispatch(addPlayerName(socketId, event.target.nickName.value)); - store.dispatch(enterLobby()); - } - - //let { isPlaying } = this.props.gameState; - // let { bugReportOpen } = this.props.controlPanel; - return ( -
-
3D TRON
-
-
- - - -
- -
-
Use "w a s d" or arrow keys to turn
-
- ); - } -} - -const mapStateToProps = ({ gameState, players, musicPlayer }) => ({ gameState, players, musicPlayer }); -const mapDispatchToProps = dispatch => ({ - enterLobby: () => dispatch(enterLobby()), - setPlayerName: e => dispatch(addPlayerName(localStorage.getItem('mySocketId'), e.target.nickName.value)), - toggleSong: event => dispatch(toggleSong()) + dispatch(enterLobby()); + }, + toggleSong: () => dispatch(toggleSong()) }); export default connect(mapStateToProps, mapDispatchToProps)(Landing); - -// onClick = { this.props.enterLobby } - -//
-// -// -//
diff --git a/browser/components/LobbyRoom.js b/browser/components/LobbyRoom.js index c37d480..c27d737 100644 --- a/browser/components/LobbyRoom.js +++ b/browser/components/LobbyRoom.js @@ -1,70 +1,39 @@ -import React, { Component } from 'react'; +'use strict'; +import React from 'react'; import { connect } from 'react-redux'; import socket from '../socket'; import Chat from './Chat'; - -//not needed yet -// import ControlPanel from './ControlPanel'; -// import BugReportForm from './BugReportForm'; - - -class LobbyRoom extends Component { - constructor(props) { - super(props); - } - - - render() { - - function readyPlayerEmitter(){ - console.log("CLICK????****") - const socketId = localStorage.getItem('mySocketId') - socket.emit('readyPlayer', socketId) - } - - let exisitingPlayers = this.props.players.filter(player => { - return player.playerName; - }) - - console.log('exisitingPlayer', exisitingPlayers) - //let { isPlaying } = this.props.gameState; - // let { bugReportOpen } = this.props.controlPanel; - return ( -
-
STAGING AREA
- -

PLAYERS ONLINE

- -
- - - - -
- ); - } -} -// players={ this.props.players } -//things we probably need... -const mapStateToProps = ({ gameState, players}) => ({ gameState, players }); -const mapDispatchToProps = dispatch => ({ - addPlayerName: e => dispatch(addPlayerName(localStorage.getItem('mySocketId'), e.target.value)) +const LobbyRoom = ({ exisitingPlayers, readyPlayerEmitter }) => ( +
+
STAGING AREA
+ +

PLAYERS ONLINE

+ +
+ + + +
+); + +const mapStateToProps = ({ gameState, players }) => ({ gameState, exisitingPlayers: players.filter(player => player.id) }); +const mapDispatchToProps = () => ({ + readyPlayerEmitter: () => socket.emit('readyPlayer', localStorage.getItem('mySocketId')) }); -export default connect( - mapStateToProps, - mapDispatchToProps -)(LobbyRoom); +export default connect(mapStateToProps, mapDispatchToProps)(LobbyRoom); diff --git a/browser/game/directionsFunctions.js b/browser/game/directionsFunctions.js index 389d5fe..8282eef 100644 --- a/browser/game/directionsFunctions.js +++ b/browser/game/directionsFunctions.js @@ -1,4 +1,4 @@ -import world, { speed } from './world'; +'use strict'; import socket from '../socket'; const turnLeft = (player) => { @@ -6,7 +6,6 @@ const turnLeft = (player) => { id: player.id, turn: 'left' }); - return player; }; const turnRight = (player) => { @@ -14,7 +13,6 @@ const turnRight = (player) => { id: player.id, turn: 'right' }); - return player; }; const turnUp = (player) => { @@ -22,7 +20,6 @@ const turnUp = (player) => { id: player.id, turn: 'up' }); - return player; }; const turnDown = (player) => { @@ -30,7 +27,11 @@ const turnDown = (player) => { id: player.id, turn: 'down' }); - return player; }; -export { turnLeft, turnRight, turnUp, turnDown }; +export const turnPlayer = (direction, me) => { + if (direction === 37 || direction === 65) turnLeft(me); + if (direction === 39 || direction === 68) turnRight(me); + if (direction === 38 || direction === 87) turnUp(me); + if (direction === 40 || direction === 83) turnDown(me); +}; diff --git a/browser/game/field.js b/browser/game/field.js index 0b5fe5d..a9cac4c 100644 --- a/browser/game/field.js +++ b/browser/game/field.js @@ -1,3 +1,4 @@ +'use strict'; const plane1 = new WHS.Box({ geometry: {width: 1000, height: 1000, depth: 0}, mass: 0, diff --git a/browser/game/gamePlayFunctions.js b/browser/game/gamePlayFunctions.js index 496ab30..98ddc37 100644 --- a/browser/game/gamePlayFunctions.js +++ b/browser/game/gamePlayFunctions.js @@ -1,7 +1,8 @@ import world, { speed } from './world'; import store from '../store'; import { scalarInitialPosition } from './players'; -import { onDeath } from '../reducers/players' +import { onDeath } from '../reducers/players'; +import { setMainPlayer, onDeathMainPlayer } from '../reducers/mainPlayer'; //rotate function export const rotate = (user) => { let ups, vs @@ -107,21 +108,37 @@ export const rotate = (user) => { }; export const collisionHandler = player => { - clearInterval(player.si) - world.scene.remove(player.ball.native) - world.scene.remove(player.bike.native) - if(player.id) store.dispatch(onDeath(player)) - if(player.walls.length !== 0) { - player.walls.forEach(wall => world.scene.remove(wall.native)) + const me = store.getState().mainPlayer; + world.scene.remove(player.ball.native); + world.scene.remove(player.bike.native); + if (player.id === me.id) { + me.ball.remove(world.camera); + world.setControls(new WHS.OrbitControls()); + store.dispatch(onDeathMainPlayer(me)); + } + + // WHEN THIS IS USED WE (used to) GET OUR PROBLEM + store.dispatch(onDeath(player)); + + if (player.walls.length !== 0) { + player.walls.forEach(wall => world.scene.remove(wall.native)); } - if(!!player.wall[0]) { - world.scene.remove(player.wall[0].native) + if (player.wall[0]) { + world.scene.remove(player.wall[0].native); } - if(player.signature===store.getState().mainPlayer.signature){ - player.ball.remove(world.camera) + if (player.signature === store.getState().mainPlayer.signature){ + player.ball.remove(world.camera); + // store.dispatch(setMainPlayer(player)); world.setControls(new WHS.OrbitControls()); } -} +}; +// export const collisionHandler = player => { +// clearInterval(player.si) +// world.scene.remove(player.ball.native) +// world.scene.remove(player.bike.native) +// console.log("HEHREHREHRHERHERHERHERHE"); +// if(player.id !== store.getState().mainPlayer.id) store.dispatch(onDeath(player)) +// } export const cameraSet = (player) => { let velocityVector = player.ball.native._physijs.linearVelocity @@ -158,4 +175,4 @@ export const cameraSetOnStart = (player) => { upVector.x*6-(player.ball.position.x/scalarInitialPosition)*speed*3, upVector.y*6-(player.ball.position.y/scalarInitialPosition)*speed*3, upVector.z*6-(player.ball.position.z/scalarInitialPosition)*speed*3)) -} \ No newline at end of file +} diff --git a/browser/game/playerConstructor.js b/browser/game/playerConstructor.js index 563e49a..12b344d 100644 --- a/browser/game/playerConstructor.js +++ b/browser/game/playerConstructor.js @@ -11,8 +11,6 @@ const sphereBase = new WHS.Sphere({ } }); - - export default function PlayerConstructor(color){ let that = this that.ball = sphereBase.clone(); diff --git a/browser/game/turnFunctions.js b/browser/game/turnFunctions.js index abccfd3..ac9e0ae 100644 --- a/browser/game/turnFunctions.js +++ b/browser/game/turnFunctions.js @@ -15,7 +15,7 @@ export const left = (player) => { player.ball.setLinearVelocity(newVel); player.ball.native._physijs.linearVelocity.set(newVel.x, newVel.y, newVel.z); player.ball.native.up.set(up2.x, up2.y, up2.z); - return rotate(player) + rotate(player) }; export const right = (player) => { @@ -27,7 +27,7 @@ export const right = (player) => { player.ball.setLinearVelocity(newVel); player.ball.native._physijs.linearVelocity.set(newVel.x, newVel.y, newVel.z); player.ball.native.up.set(up2.x, up2.y, up2.z); - return rotate(player) + rotate(player) }; export const up = (player) => { @@ -44,7 +44,7 @@ export const up = (player) => { player.ball.setLinearVelocity({x: vx, y: vy, z: vz}); player.ball.native._physijs.linearVelocity.set(vx, vy, vz); player.ball.native.up.set(up2.x, up2.y, up2.z); - return rotate(player); + rotate(player); }; export const down = (player) => { @@ -61,5 +61,5 @@ export const down = (player) => { player.ball.setLinearVelocity({x: vx, y: vy, z: vz}); player.ball.native._physijs.linearVelocity.set(vx, vy, vz); player.ball.native.up.set(up2.x, up2.y, up2.z); - return rotate(player); + rotate(player); }; diff --git a/browser/game/world.js b/browser/game/world.js index 131a50f..ee1aa89 100644 --- a/browser/game/world.js +++ b/browser/game/world.js @@ -1,3 +1,4 @@ +'use strict'; // const WHS = require('whitestormjs'); import { field } from './field'; diff --git a/browser/reducers/gameState.js b/browser/reducers/gameState.js index 4a2bd9b..7139a23 100644 --- a/browser/reducers/gameState.js +++ b/browser/reducers/gameState.js @@ -1,3 +1,4 @@ +'use strict'; /*-------- ACTION TYPES ---------*/ //const IS_DISPLAYED = 'IS_DISPLAYED'; @@ -12,7 +13,6 @@ const initialState = { isEnter: true }; - export const startGame = () => ({ type: START_GAME, }); @@ -23,11 +23,7 @@ export const stopGame = () => ({ export const enterLobby = () => ({ type: ENTER_LOBBY -}) - - - - +}); /*-------REDUCER------------*/ diff --git a/browser/reducers/index.js b/browser/reducers/index.js index 9b597b7..57e2654 100644 --- a/browser/reducers/index.js +++ b/browser/reducers/index.js @@ -1,3 +1,4 @@ +'use strict'; import { combineReducers } from 'redux'; import players from './players'; import mainPlayer from './mainPlayer'; diff --git a/browser/reducers/mainPlayer.js b/browser/reducers/mainPlayer.js index d46fca2..07c86be 100644 --- a/browser/reducers/mainPlayer.js +++ b/browser/reducers/mainPlayer.js @@ -1,10 +1,7 @@ -import { turnLeft, turnRight, turnUp, turnDown } from '../game/directionsFunctions'; +'use strict'; /*---------- ACTION TYPES ----------*/ const SET_MAIN_PLAYER = 'SET_MAIN_PLAYER'; -const TURN_PLAYER_LEFT = 'TURN_PLAYER_LEFT'; -const TURN_PLAYER_RIGHT = 'TURN_PLAYER_RIGHT'; -const TURN_PLAYER_UP = 'TURN_PLAYER_UP'; -const TURN_PLAYER_DOWN = 'TURN_PLAYER_DOWN'; +const MAIN_PLAYER_DEATH = 'MAIN_PLAYER_DEATH'; /*---------- ACTION CREATORS ----------*/ export const setMainPlayer = (player) => ({ @@ -12,14 +9,10 @@ export const setMainPlayer = (player) => ({ player }); -export const turnPlayer = (direction) => { - let type; - if (direction === 37 || direction === 65) type = TURN_PLAYER_LEFT; - if (direction === 39 || direction === 68) type = TURN_PLAYER_RIGHT; - if (direction === 38 || direction === 87) type = TURN_PLAYER_UP; - if (direction === 40 || direction === 83) type = TURN_PLAYER_DOWN; - return { type }; -}; +export const onDeathMainPlayer = (me) => ({ + type: MAIN_PLAYER_DEATH, + me +}); /*---------- THUNK CREATORS ----------*/ @@ -31,14 +24,10 @@ export default (mainPlayer = {}, action) => { switch (action.type) { case SET_MAIN_PLAYER: return action.player; - case TURN_PLAYER_LEFT: - return turnLeft(mainPlayer); - case TURN_PLAYER_RIGHT: - return turnRight(mainPlayer); - case TURN_PLAYER_UP: - return turnUp(mainPlayer); - case TURN_PLAYER_DOWN: - return turnDown(mainPlayer); + case MAIN_PLAYER_DEATH: + // newPlayer.si = 0; + newPlayer.status = 'dead'; + return newPlayer; default: return mainPlayer; } diff --git a/browser/reducers/messages.js b/browser/reducers/messages.js index 7473ee9..3797e95 100644 --- a/browser/reducers/messages.js +++ b/browser/reducers/messages.js @@ -1,3 +1,4 @@ +'use strict'; /*---------- INITIAL STATE ----------*/ const initialState = []; diff --git a/browser/reducers/musicPlayer.js b/browser/reducers/musicPlayer.js index 37fd0ca..0a500f1 100644 --- a/browser/reducers/musicPlayer.js +++ b/browser/reducers/musicPlayer.js @@ -1,3 +1,4 @@ +'use strict'; const AUDIO = document.createElement('audio'); AUDIO.src = 'mp3/SoundTrack.mp3'; AUDIO.load(); @@ -6,7 +7,7 @@ AUDIO.autoplay = true; /*---------- INITIAL STATE ----------*/ const initialState = { songPlaying: true - } +}; /*---------- ACTION TYPES ----------*/ const START_PLAYING = 'START_PLAYING'; @@ -27,7 +28,6 @@ export const pause = () => { }; }; - export const toggleSong = () => { return (dispatch, getState) => { const currentState = getState().musicPlayer; @@ -39,9 +39,6 @@ export const toggleSong = () => { }; }; - - - /*---------- THUNK CREATORS ----------*/ /*---------- REDUCER ----------*/ diff --git a/browser/reducers/players.js b/browser/reducers/players.js index cd64ad0..aadd1dc 100644 --- a/browser/reducers/players.js +++ b/browser/reducers/players.js @@ -1,7 +1,5 @@ +'use strict'; import allPlayers from '../game/players'; -import world from '../game/world'; -import { rotate } from '../game/gamePlayFunctions'; -import store from '../store'; /*---------- INITIAL STATE ----------*/ const initialState = allPlayers; @@ -10,8 +8,8 @@ const initialState = allPlayers; const SET_PLAYER_ID = 'SET_PLAYER_ID'; const ADD_PLAYER_NAME = 'ADD_PLAYER_NAME'; const REMOVE_PLAYER = 'REMOVE_PLAYER'; -const ON_DEATH = 'ON_DEATH' -const DECLARE_WINNER = 'DECLARE_WINNER' +const ON_DEATH = 'ON_DEATH'; +const DECLARE_WINNER = 'DECLARE_WINNER'; /*---------- ACTION CREATORS ----------*/ export const setPlayerId = (users) => ({ @@ -25,7 +23,6 @@ export const addPlayerName = (playerId, playerName) => ({ playerName }); - export const removePlayer = (userId) => ({ type: REMOVE_PLAYER, userId @@ -36,10 +33,10 @@ export const onDeath = (player) => ({ player }); -export const declareWinner = (player) => ({ +export const declareWinner = (playerId) => ({ type: DECLARE_WINNER, - player -}) + playerId +}); /*---------- THUNK CREATORS ----------*/ @@ -60,15 +57,12 @@ export default (players = initialState, action) => { return newPlayers; case ADD_PLAYER_NAME: - // console.log('PLAYER WITH NEW NAME 1', players) - let playerWithNewName = players.map((player) => { + return players.map((player) => { if (player.id === action.playerId) { player.playerName = action.playerName; } return player; }); - // console.log('PLAYER WITH NEW NAME2', playerWithNewName) - return playerWithNewName case REMOVE_PLAYER: return players.map((bike) => { @@ -80,19 +74,20 @@ export default (players = initialState, action) => { case ON_DEATH: return players.map((player) => { - if(player.signature === action.player.signature){ - player.status = 'dead' + if (player.signature === action.player.signature){ + clearInterval(player.si); + player.status = 'dead'; } - return player + return player; }); case DECLARE_WINNER: return players.map((player) => { - if(player.signature === action.player.signature){ - player.winner = true + if (player.id === action.playerId){ + player.winner = true; } - return player - }) + return player; + }); default: return players; } diff --git a/browser/socket.js b/browser/socket.js index 590664e..a2ea573 100644 --- a/browser/socket.js +++ b/browser/socket.js @@ -1,12 +1,12 @@ +'use strict'; import store from './store'; -import { setPlayerId, updatePlayer, addPlayerName, removePlayer } from './reducers/players'; -import { startGame, stopGame} from './reducers/gameState'; +import { setPlayerId, addPlayerName, removePlayer, declareWinner } from './reducers/players'; +import { startGame } from './reducers/gameState'; import { setMainPlayer } from './reducers/mainPlayer'; import { receiveMessage } from './reducers/messages'; import { left, right, up, down } from './game/turnFunctions'; -import world, { speed } from './game/world'; import { cameraSet, collisionHandler } from './game/gamePlayFunctions'; -import { declareWinner } from './reducers/players'; +import world from './game/world'; const socket = io('/'); @@ -22,31 +22,24 @@ export const initializeSocket = () => { socket.on('addUser', (allUsers) => { store.dispatch(setPlayerId(allUsers)); - console.log('ALL USERS ****', allUsers) - allUsers.forEach(user => { - store.dispatch(addPlayerName(user.id, user.playerName)) - }) const myUser = allUsers.find(user => user.id === localStorage.getItem('mySocketId')); const myBike = allBikes.find(bike => bike.id === myUser.id); store.dispatch(setMainPlayer(myBike)); }); - socket.on('addPlayerName', (socketId, playerName) =>{ - console.log("ADD OTHER PLAYERS NAME", socketId, playerName); + socket.on('addPlayerName', (socketId, playerName) => { store.dispatch(addPlayerName(socketId, playerName)); }); socket.on('addNewMessage', (text, senderName) => { - console.log("RECEIVE MESSAGE & SENDERNAME FRONTEND ***", text, senderName); - store.dispatch(receiveMessage({text:text, name:senderName})); - + store.dispatch(receiveMessage({text: text, name: senderName})); }); - socket.on('startGame', () => { allBikes.forEach(player => { - if (!player.id){ - collisionHandler(player); + if (!player.id) { + world.scene.remove(player.ball.native); + world.scene.remove(player.bike.native); } }); store.dispatch(startGame()); @@ -77,23 +70,19 @@ export const initializeSocket = () => { }); socket.on('ball-collision-to-handle', playerData => { - const playerToRemove = store.getState().players.find(player => player.signature === playerData.signature); + const playerToRemove = store.getState().players.find(player => player.id === playerData.id); collisionHandler(playerToRemove); }); socket.on('removePlayer', userId => { - console.log("ARE WE REMOVING PLAYER ON THE FRONT END?") store.dispatch(removePlayer(userId)); }); - socket.on('endGame', () => { - // store.dispatch(stopGame()); - let lastStanding = store.getState().players.filter(player => player.status === 'alive')[0] - console.log("lastStanding", lastStanding) - store.dispatch(declareWinner(lastStanding)) - // store.dispatch(stopGame()); - setTimeout(()=> window.location.reload(true), 10000) - + socket.on('endGame', (lastStanding) => { + // console.log("lastStanding", lastStanding); + store.dispatch(declareWinner(lastStanding)); + // console.log("END GAME", store.getState().players); + setTimeout(() => window.location.reload(true), 10000); }); }; diff --git a/browser/store.js b/browser/store.js index 0152b85..e746664 100644 --- a/browser/store.js +++ b/browser/store.js @@ -1,9 +1,9 @@ +'use strict'; import { createStore, applyMiddleware } from 'redux'; import createLogger from 'redux-logger'; import thunkMiddleware from 'redux-thunk'; -import { composeWithDevTools } from 'redux-devtools-extension'; import rootReducer from './reducers'; -const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(createLogger(), thunkMiddleware))); +const store = createStore(rootReducer, applyMiddleware(createLogger(), thunkMiddleware)); export default store; diff --git a/package.json b/package.json index dc49702..1c9cca9 100644 --- a/package.json +++ b/package.json @@ -27,20 +27,10 @@ "bcrypt": "^0.8.7", "bluebird": "^3.4.6", "body-parser": "^1.15.2", - "cannon": "github:schteppe/cannon.js", "chalk": "^1.1.3", - "classnames": "^2.2.5", - "cookie-session": "^2.0.0-alpha.2", "express": "^4.14.0", - "jquery": "^3.1.1", - "json-loader": "^0.5.4", "lodash": "^4.17.4", - "materialize-css": "^0.97.8", "mathjs": "^3.9.0", - "morgan": "^1.7.0", - "nodemon": "^1.11.0", - "passport": "^0.3.2", - "passport-local": "^1.0.0", "pg": "^6.1.0", "pg-hstore": "^2.3.2", "react": "^15.4.2", @@ -49,14 +39,11 @@ "react-redux": "^4.4.6", "react-router": "^3.0.0", "redux": "^3.6.0", - "redux-devtools-extension": "^1.0.0", "redux-logger": "^2.7.4", "redux-thunk": "^2.1.0", "sequelize": "^3.26.0", "socket.io": "^1.7.2", "swearjar": "^0.1.3", - "three": "^0.82.1", - "three.js": "^0.77.1", "volleyball": "^1.4.1", "webpack": "^1.13.3", "whitestormjs": "^0.11.2", @@ -68,9 +55,9 @@ "enzyme": "^2.7.1", "mocha": "^2.3.3", "react-addons-test-utils": "^15.4.0", - "redux-devtools-extension": "^1.0.0", "sinon": "^1.17.7", "supertest": "^1.1.0", - "supertest-as-promised": "^3.1.0" + "supertest-as-promised": "^3.1.0", + "nodemon": "^1.11.0" } } diff --git a/public/index.html b/public/index.html index 04189c1..5893f08 100644 --- a/public/index.html +++ b/public/index.html @@ -1,4 +1,3 @@ - @@ -7,9 +6,9 @@ - + - + diff --git a/public/stylesheet.css b/public/stylesheet.css index 9600adf..6a1b153 100644 --- a/public/stylesheet.css +++ b/public/stylesheet.css @@ -1,12 +1,12 @@ body, html { - height: 100%; - color: #ffffff; - font-size: 1em; - font-family: "Quicksand", sans-serif; - overflow: hidden; + height: 100%; + color: #ffffff; + font-size: 1em; + font-family: "Quicksand", sans-serif; + overflow: hidden; - background: url('./images/bluebike.jpg') no-repeat top center fixed; - background-size: cover; + /*background: url('./images/bluebike.jpg') no-repeat top center fixed;*/ + background-size: cover; } button:focus { @@ -54,70 +54,70 @@ button:focus { } #name-box{ - font-weight: bold; - color: white; - position: fixed; - font-size: 4vh; - top: 55%; - left: 50%; - transform: translate(-50%, -50%); - width: 25vw; - height: 7vh; - border: 1px solid; - border-radius: 8px; - background-color: transparent; - text-align: center; + font-weight: bold; + color: white; + position: fixed; + font-size: 4vh; + top: 55%; + left: 50%; + transform: translate(-50%, -50%); + width: 25vw; + height: 7vh; + border: 1px solid; + border-radius: 8px; + background-color: transparent; + text-align: center; } #play-box { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - color: white; - position: fixed; - font-size: 3.5vh; - top: 65%; - left: 50%; - transform: translate(-50%, -50%); - border: 1px solid; - border-radius: 14px; - background-color: transparent; - text-align: center; - padding: 1vh 1vw; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: white; + position: fixed; + font-size: 3.5vh; + top: 65%; + left: 50%; + transform: translate(-50%, -50%); + border: 1px solid; + border-radius: 14px; + background-color: transparent; + text-align: center; + padding: 1vh 1vw; } #join-box { -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - color: white; - position: fixed; - font-size: 2.5vh; - top: 91%; - left: 82.5%; - transform: translate(-50%, -50%); - border: 1px solid; - border-radius: 6px; - background-color: transparent; - text-align: center; - padding: 2vh 2vw; - z-index: 3; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: white; + position: fixed; + font-size: 2.5vh; + top: 91%; + left: 82.5%; + transform: translate(-50%, -50%); + border: 1px solid; + border-radius: 6px; + background-color: transparent; + text-align: center; + padding: 2vh 2vw; + z-index: 3; } #music-box { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - color: white; - position: fixed;font-size: 2vh; - top: 90%; - left: 10%; - transform: translate(-50%, -50%); - border: 1px solid; - border-radius: 14px; - background-color: transparent; - text-align: center; - padding: 1vh 1vw; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + color: white; + position: fixed;font-size: 2vh; + top: 90%; + left: 10%; + transform: translate(-50%, -50%); + border: 1px solid; + border-radius: 14px; + background-color: transparent; + text-align: center; + padding: 1vh 1vw; } .glyphicon { @@ -156,9 +156,9 @@ ul { } #listName { -color: #c8c8c8; -background-color:#303030; -text-align: left + color: #c8c8c8; + background-color:#303030; + text-align: left } .listName-item{ @@ -169,39 +169,39 @@ text-align: left } #chat-box { - transform: scale(1.1,1.1); - font-size: 1.1em; - bottom: 20%; - margin-left: 18%; - position: fixed; - width: 75%; - max-width: 80em; - height: 60%; - z-index: 1; - float: right; - background: black; - border: 2px solid white; - border-radius: 10px; + transform: scale(1.1,1.1); + font-size: 1.1em; + bottom: 20%; + margin-left: 18%; + position: fixed; + width: 75%; + max-width: 80em; + height: 60%; + z-index: 1; + float: right; + background: black; + border: 2px solid white; + border-radius: 10px; } #chat-bar { - height: 3rem; - position: absolute; - bottom: -29%; - left: 3.9%; - width: 82%; - height: 12%; - transform: scale(1.1, 1.1); - background-color: grey; - margin-bottom: 5%; - box-sizing: inherit; - padding-top: 0.5em; - padding-left: 2.5%; - border-left: 1px solid #aaa; - border-bottom: 1px solid #aaa; - border-right: 1px solid #aaa; - border-radius: 10px; - color: black; + height: 3rem; + position: absolute; + bottom: -29%; + left: 3.9%; + width: 82%; + height: 12%; + transform: scale(1.1, 1.1); + background-color: grey; + margin-bottom: 5%; + box-sizing: inherit; + padding-top: 0.5em; + padding-left: 2.5%; + border-left: 1px solid #aaa; + border-bottom: 1px solid #aaa; + border-right: 1px solid #aaa; + border-radius: 10px; + color: black; } #message-box { diff --git a/server/index.js b/server/index.js index 0b46594..eb82798 100644 --- a/server/index.js +++ b/server/index.js @@ -6,7 +6,6 @@ const express = require('express'); const volleyball = require('volleyball'); const bodyParser = require('body-parser'); const app = express(); -var socketio = require('socket.io'); const ioInit = require('./serverSockets'); diff --git a/server/reducers/users.js b/server/reducers/users.js index 5b8a0a7..0719213 100644 --- a/server/reducers/users.js +++ b/server/reducers/users.js @@ -1,5 +1,5 @@ +'use strict'; /* --------------- ACTIONS --------------- */ - const ADD_USER = 'ADD_USER'; const REMOVE_USER = 'REMOVE_USER'; const READY_PLAYER = 'READY_PLAYER'; @@ -18,7 +18,7 @@ const removeUser = (userId) => ({ userId }); -const readyPlayer = (playerId) => ({ +const startReady = (playerId) => ({ type: READY_PLAYER, playerId }); @@ -32,7 +32,7 @@ const addUserName = (userId, playerName) => ({ type: ADD_USER_NAME, userId, playerName -}) +}); /* --------------- THUNK ACTION CREATORS --------------- */ @@ -51,22 +51,17 @@ const removeUserAndEmit = socket => { }; }; -const startReady = playerId => { - return dispatch => { - dispatch(readyPlayer(playerId)); - }; -}; /* --------------- REDUCER --------------- */ const initialState = [ - {id: ''}, - {id: ''}, - {id: ''}, - {id: ''}, - {id: ''}, - {id: ''} + {id: '', active: false, readyToPlay: false}, + {id: '', active: false, readyToPlay: false}, + {id: '', active: false, readyToPlay: false}, + {id: '', active: false, readyToPlay: false}, + {id: '', active: false, readyToPlay: false}, + {id: '', active: false, readyToPlay: false} ]; -function userReducer (state = initialState, action) { +function userReducer(state = initialState, action) { const newUser = [...state]; @@ -83,13 +78,13 @@ function userReducer (state = initialState, action) { return newUser; case READY_PLAYER: - return state.map((user) => { - if (user.id === action.playerId) { - user.readyToPlay = true; - user.active = true; - } - return user; - }); + return state.map((user) => { + if (user.id === action.playerId) { + user.readyToPlay = true; + user.active = true; + } + return user; + }); case REMOVE_USER: return newUser.map(user => { @@ -127,7 +122,6 @@ module.exports = { addUser, REMOVE_USER, removeUser, - readyPlayer, createAndEmitUser, removeUserAndEmit, userReducer, diff --git a/server/serverSockets/index.js b/server/serverSockets/index.js index 86b8b9d..f5ba844 100644 --- a/server/serverSockets/index.js +++ b/server/serverSockets/index.js @@ -1,3 +1,4 @@ +'use strict'; const socketio = require('socket.io'); let IO = null; diff --git a/server/serverSockets/socket.js b/server/serverSockets/socket.js index abe1f77..5d88bf6 100644 --- a/server/serverSockets/socket.js +++ b/server/serverSockets/socket.js @@ -1,3 +1,4 @@ +'use strict'; const chalk = require('chalk'); const store = require('../store'); const { @@ -16,36 +17,27 @@ module.exports = io => { // New user enters; create new user and new user appears for everyone else store.dispatch(createAndEmitUser(socket)); const allUsers = store.getState().users; - console.log(allUsers) io.sockets.emit('addUser', allUsers); //Player ready in landing page //We need to update this so that game starting works smoothly - socket.on('playerName', (socketId, playerName) => { store.dispatch(addUserName(socketId, playerName)); - socket.broadcast.emit('addPlayerName', socketId, playerName); + io.sockets.emit('addPlayerName', socketId, playerName); }); - socket.on('newMessage', (message, socketId)=>{ - let getSender = store.getState().users.find(user => { - return user.id === socketId - }); + socket.on('newMessage', (message, socketId) => { + let getSender = store.getState().users.find(user => user.id === socketId); io.sockets.emit('addNewMessage', message, getSender.playerName); - }) + }); socket.on('readyPlayer', (playerId) => { store.dispatch(startReady(playerId)); - let checkReadyUsers = store.getState().users.filter(user => user.id !== '' ); - -// gamePlay -// if (checkReadyUsers.length > 1 && - + let checkReadyUsers = store.getState().users.filter(user => user.id); -// test for debug if (checkReadyUsers.length >= 1 && - checkReadyUsers.length === checkReadyUsers.filter(user => user.readyToPlay === true).length) { + checkReadyUsers.length === checkReadyUsers.filter(user => user.readyToPlay).length) { // if (users.filter(user => user.readyToPlay).length === 3) { io.sockets.emit('startGame'); } @@ -56,6 +48,7 @@ module.exports = io => { //Here the back end recognizes that a ball collided and sends out a syncronized message to all users to handle the collision. socket.on('ball-collision', (playerData) => { + console.log("COLLISON"); io.sockets.emit('ball-collision-to-handle', playerData); if (playerData.id) { console.log(chalk.red(playerData.id)); @@ -63,8 +56,9 @@ module.exports = io => { console.log(chalk.red(store.getState().users.filter(user => user.active === true).length)); } - if (store.getState().users.filter(user => user.active === true).length === 1){ - io.sockets.emit('endGame'); + const alivePlayers = store.getState().users.filter(user => user.active); + if (alivePlayers.length <= 1) { + io.sockets.emit('endGame', alivePlayers[0] ? alivePlayers[0].id : store.getState().users[0].id); } }); @@ -75,7 +69,7 @@ module.exports = io => { socket.on('disconnect', () => { store.dispatch(removeUserAndEmit(socket)); - io.sockets.emit('removePlayer', socket.id) + io.sockets.emit('removePlayer', socket.id); console.log(chalk.magenta(`${socket.id} has disconnected`)); }); }); diff --git a/server/store.js b/server/store.js index ff63be1..409aa64 100644 --- a/server/store.js +++ b/server/store.js @@ -1,3 +1,4 @@ +'use strict'; const { createStore, applyMiddleware, combineReducers } = require('redux'); const thunkMiddleware = require('redux-thunk').default; diff --git a/tests/browser/react-redux/reducers/gameState.js b/tests/browser/react-redux/reducers/gameState.js index d233d96..40f90ec 100644 --- a/tests/browser/react-redux/reducers/gameState.js +++ b/tests/browser/react-redux/reducers/gameState.js @@ -12,7 +12,8 @@ describe("|----- FRONTEND Game State Reducer -----|", () => { it('has proper initial state', () => { expect(testStore.getState()).to.be.deep.equal({ - isPlaying: false + isPlaying: false, + isEnter: true }); }); diff --git a/tests/server/redux/actions/users.js b/tests/server/redux/actions/users.js index ccb73e2..77386a3 100644 --- a/tests/server/redux/actions/users.js +++ b/tests/server/redux/actions/users.js @@ -1,6 +1,6 @@ import {expect} from 'chai'; -import {addUser, removeUser, readyPlayer, playerCollision} from '../../../../server/reducers/users'; +import {addUser, removeUser, startReady, playerCollision} from '../../../../server/reducers/users'; describe("|----- BACKEND User Actions -----|", () => { const testUserId = '12345'; @@ -20,7 +20,7 @@ describe("|----- BACKEND User Actions -----|", () => { }); it('Sets user to ready', () => { - expect(readyPlayer(testUserId)).to.be.deep.equal({ + expect(startReady(testUserId)).to.be.deep.equal({ type: 'READY_PLAYER', playerId: testUserId }); diff --git a/tests/server/redux/reducers/users.js b/tests/server/redux/reducers/users.js index bd2a819..fabcb13 100644 --- a/tests/server/redux/reducers/users.js +++ b/tests/server/redux/reducers/users.js @@ -14,7 +14,7 @@ describe("|----- BACKEND User Reducer -----|", () => { it('has proper initial state', () => { expect(testStore.getState()).to.be.deep.equal([ - {id: ''}, {id: ''}, {id: ''}, {id: ''}, {id: ''}, {id: ''} + {id: '', active: false, readyToPlay: false}, {id: '', active: false, readyToPlay: false}, {id: '', active: false, readyToPlay: false}, {id: '', active: false, readyToPlay: false}, {id: '', active: false, readyToPlay: false}, {id: '', active: false, readyToPlay: false} ]); });