diff --git a/client/src/App.js b/client/src/App.js index 4c1507c..b15fdfe 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -9,8 +9,38 @@ const socket = io('http://localhost:7777'); var id = ''; var drawing_app = null; -var game_info = document.getElementById('game_state_info'); -var pixi_wrapper = document.getElementById('pixi-wrapper'); +var did_vote = false; + +const game_info = document.getElementById('game_state_info'); +const pixi_wrapper = document.getElementById('pixi-wrapper'); + +const join_room_btn = document.getElementById('join_room_btn'); + +const room_code_input = document.getElementById('room_code'); +const username_input = document.getElementById('username'); + +const game_room_code_label = document.getElementById('game_room_code'); + +const game_setup_panel = document.getElementById('game_setup'); +const game_info_panel = document.getElementById('game_info'); + +const copy_image_btn = document.getElementById('copy_image'); +const download_image_btn = document.getElementById('download_image'); + +const current_player_count = document.getElementById('current_player_count'); +const player_list = document.getElementById('player_list'); +const pre_game_player_list = document.getElementById('pre_game_player_list'); + +const ready_up_btn = document.getElementById('ready_up_btn'); +const create_room_btn = document.getElementById('create_room_btn'); + +const game_leader_input = document.getElementById('game_leader_input'); + +const game_hint = document.getElementById('game_hint'); +const game_topic = document.getElementById('game_topic'); +const submit_topic_btn = document.getElementById('submit_topic'); + +const voting_player_list = document.getElementById('voting_player_list'); function setGameInfo(info) { game_info.innerHTML = info; @@ -24,87 +54,75 @@ socket.on('connect', () => { console.log('\n*** Startup! ***'); }); -document.getElementById('join_room_btn').addEventListener('click', (e) => { - var room_code = document.getElementById('room_code').value; - var username = document.getElementById('username').value; - - socket.emit('join_room', room_code, username); +join_room_btn.addEventListener('click', (e) => { + socket.emit('join_room', room_code_input.value, username_input.value); }); -socket.on('room', (room_id) => { +socket.on('join_room', (room_id) => { // join the room console.log(room_id); - document.getElementById('game_room_code').innerHTML = room_id; + game_room_code_label.innerHTML = room_id; - document.getElementById('game_setup').style.display = 'none'; - document.getElementById('game_info').style.display = 'initial'; + game_setup_panel.style.display = 'none'; + game_info_panel.style.display = 'initial'; - document.getElementById('copy_image').style.display = 'none'; - document.getElementById('download_image').style.display = 'none'; + copy_image_btn.style.display = 'none'; + download_image_btn.style.display = 'none'; }); socket.on('room_error', (error_msg) => { alert(error_msg); }); -document.getElementById('create_room_btn').addEventListener('click', (e) => { +socket.on('topic_error', (error_msg) => { + alert(error_msg); +}); + +create_room_btn.addEventListener('click', (e) => { // Tell the server we want a new room - var username = document.getElementById('username').value; - socket.emit('create_room', username); + socket.emit('create_room', username_input.value); }); socket.on('player_data', (player_arr) => { + console.log('Playerdata!?'); var count = player_arr.length; - document.getElementById('current_player_count').innerHTML = count; - - var player_list = document.getElementById('player_list'); + current_player_count.innerHTML = count; player_list.innerHTML = ''; player_arr.forEach((player) => { - player_list.innerHTML += '
  • ' + player.player_name + '
  • '; + player_list.innerHTML += + `
  • ${player.player_name}
  • `; }) - document.getElementById('start_game_btn').disabled = (count < 2); + ready_up_btn.disabled = (count < 2); }); -document.getElementById('start_game_btn').addEventListener('click', (e) => { - socket.emit('start_game'); +ready_up_btn.addEventListener('click', (e) => { + socket.emit('ready'); }); socket.on('game_started', () => { - // game_data.game_leader = - console.log('yooo'); - document.getElementById('pre_game_player_list').style.display = 'none'; + voting_player_list.innerHTML = ""; + document.getElementById('pl_container').style.display = "none"; + document.getElementById('copy_image').style.display = 'none'; + document.getElementById('download_image').style.display = 'none'; + + pixi_wrapper.innerHTML = ""; + drawing_app = null; + + pre_game_player_list.style.display = 'none'; }); socket.on('leader_selected', (leader) => { - console.log(leader); - console.log(id); setGameInfo(`Please wait while the leader ${ - leader.player_name} is selecting a hint to be painted`); + leader.player_name} is thinking of a topic to be painted`); if (leader.id == id) { setGameInfo( 'You are the game leader! Please choose a hint and a category that the hint fits in.'); - document.getElementById('game_leader_input').style.display = 'initial'; - - const game_hint = document.getElementById('game_hint'); - const game_topic = document.getElementById('game_topic'); - const submit_topic_btn = document.getElementById('submit_topic'); - - function updateSubmitButtonState() { - submit_topic_btn.disabled = - game_hint.value.length < 2 || game_topic.value.length < 2; - } - - game_hint.addEventListener('input', (e) => { - updateSubmitButtonState(); - }); - - game_topic.addEventListener('input', (e) => { - updateSubmitButtonState(); - }); - + game_leader_input.style.display = 'initial'; submit_topic_btn.addEventListener('click', (e) => { socket.emit('topic_selected', game_topic.value, game_hint.value); @@ -112,28 +130,118 @@ socket.on('leader_selected', (leader) => { } }); +function updateSubmitButtonState() { + submit_topic_btn.disabled = + game_hint.value.length < 2 || game_topic.value.length < 2; +} + +game_hint.addEventListener('input', (e) => { + updateSubmitButtonState(); +}); + +game_topic.addEventListener('input', (e) => { + updateSubmitButtonState(); +}); + socket.on('topic_selected', (topic, hint) => { console.log(topic, hint); - document.getElementById('game_leader_input').style.display = 'none'; + game_leader_input.style.display = 'none'; setGameInfo(`Hint: ${hint}
    Topic: ${topic}`); }); socket.on('game_finished', (leader, player_list) => { - var pl = document.getElementById('endgame_player_list'); + did_vote = false; + drawing_app.addBackgroundForDownload(); - pl.innerHTML = ''; + document.getElementById('pl_container').style.display = 'flex'; + document.getElementById('copy_image').style.display = 'initial'; + document.getElementById('download_image').style.display = 'initial'; + document.getElementById('current_user_text').style.display = 'none'; - player_list.forEach((player) => { - if (player.id == leader.id) { - pl.innerHTML += `
  • ${ - player.player_name} (Leader)
  • `; - } else { - pl.innerHTML += `
  • ${ - player.player_name}
  • `; + var idx = 1; + + player_list.forEach((p) => { + var tr = voting_player_list.insertRow(-1); + + var c1 = tr.insertCell(-1); + c1.innerHTML = `?`; + + var c2 = tr.insertCell(-1); + + // leader should not be able to vote + // spectators should not be able to vote + // basically, only show voting button if this players' id is in the list of + // voteable players + if (player_list.filter(e => e.id === id).length > 0) { + var vote_btn = document.createElement('input'); + vote_btn.type = 'button'; + vote_btn.value = 'vote'; + vote_btn.classList.add('vote_btn'); + vote_btn.addEventListener('click', () => { + console.log(p.id); + socket.emit('vote_for_player', p.id); + + Array.from(document.getElementsByClassName('vote_btn')) + .forEach((element) => { + element.disabled = true; + element.style.display = 'none'; + }) + }); + c2.appendChild(vote_btn); } - }) - pl.style.display = 'initial'; + var c3 = tr.insertCell(-1); + c3.innerHTML = `${ + p.player_name}`; + + var c4 = tr.insertCell(-1); + c4.innerHTML = `${ + idx}, ${idx + player_list.length}`; + + var c5 = tr.insertCell(-1); + var chkbox = document.createElement('input'); + chkbox.type = 'checkbox'; + chkbox.disabled = true; + chkbox.id = `votestatus_${p.id}`; + + c5.appendChild(chkbox); + // c5.innerHTML = ``; + + idx++; + }); + + socket.on('show_vote_results', (votes) => { + votes.players_voted.forEach((pv) => { + var e = document.getElementById(`votestatus_${pv}`); + if (e) { + e.checked = true; + } + + Object.entries(votes.player_votes).forEach(([k, v]) => { + var e = document.getElementById(`votecount_${k}`); + if (e) { + e.innerHTML = v; + } + }); + + var fake = document.getElementById(`playername_${votes.fake_artist.id}`); + if(fake) { + fake.innerHTML = `>>> ${votes.fake_artist.player_name} <<<`; + } + }); + + pre_game_player_list.style.display = "initial"; + }); + + socket.on('update_votes', (votes) => { + // update player vote display + votes.players_voted.forEach((pv) => { + var e = document.getElementById(`votestatus_${pv}`); + if (e) { + e.checked = true; + } + }); + }); var ink_bar = document.getElementById('myBar'); ink_bar.style.display = 'none'; @@ -186,32 +294,32 @@ socket.on('game_finished', (leader, player_list) => { base64_image.then((str) => { try { - navigator.clipboard.write([ - new ClipboardItem({ - 'image/png': b64toBlob(str) - }) - ]); - } catch (error) { + navigator.clipboard.write( + [new ClipboardItem({'image/png': b64toBlob(str)})]); + } catch (error) { console.error(error); - } + } }); }); }); socket.on('actually_start_game', (player_data) => { // Get player with our id to get the correct color + document.getElementById('current_user_text').style.display = 'flex'; + var ink_bar = document.getElementById('myBar'); + ink_bar.style.display = 'initial'; drawing_app = new DrawingApp(pixi_wrapper, player_data); window.drawing_app = drawing_app; }); -socket.on('draw_data', (round_num, data) => { - console.log('Draw Data: ', round_num); - var cur_layer = drawing_app.layers[round_num]; +socket.on('draw_data', (turn_num, data) => { + console.log('Draw Data: ', turn_num); + var cur_layer = drawing_app.layers[turn_num]; cur_layer.drawPointLine(data[0], data[1], true); }); -socket.on('advance_round', (round_num, current_player) => { +socket.on('advance_turn', (turn_num, current_player) => { var ink_bar = document.getElementById('myBar'); ink_bar.style.width = '100%'; @@ -226,7 +334,7 @@ socket.on('advance_round', (round_num, current_player) => { ink_bar.style.backgroundColor = current_player.player_color; ink_bar.style.visibility = 'visible'; - const cur_layer = drawing_app.layers[round_num]; + const cur_layer = drawing_app.layers[turn_num]; cur_layer.enable(); @@ -235,14 +343,14 @@ socket.on('advance_round', (round_num, current_player) => { ink_bar.style.width = ((1.0 - ink_pct) * 100) + '%'; - console.log(ink_pct); + console.log(ink_pct, old_pos, new_pos); socket.emit('draw_data', [old_pos, new_pos]); }); cur_layer.setFinishedPaintingCallback(() => { cur_layer.disable(); - socket.emit('round_finished'); + socket.emit('turn_finished'); }); /*player_data.forEach((p) => { diff --git a/client/src/DrawingApp.js b/client/src/DrawingApp.js index 05c04c9..7224c25 100644 --- a/client/src/DrawingApp.js +++ b/client/src/DrawingApp.js @@ -116,22 +116,22 @@ export class Layer { y: oldPos.y - newPos.y, }; const deltaLength = Math.sqrt(delta.x ** 2 + delta.y ** 2); + this.ink_used += deltaLength; if (!force) { - this.ink_used += deltaLength; + console.log("Force?"); this.live_paint_progress_cb(oldPos, newPos); } if (this.ink_used >= max_ink_per_layer) { this.lifted = true; this.drawingStarted = false; - this.finished_painting_cb(); + if(!force) { + this.finished_painting_cb(); + } return; } - // TODO: Pass socket.io to tell server? - // Or save to array and just pass to server on round-end. Should be easier? - this.drawPoint(newPos.x, newPos.y); if (deltaLength >= brush_size / 8) { @@ -170,7 +170,6 @@ export class Layer { export class DrawingApp { constructor(dom_elem, player_data) { - var self = this; this.app = new PIXI.Application({ width: layer_width, height: layer_height, @@ -182,13 +181,6 @@ export class DrawingApp { this.layers = []; - const base_render_texture = PIXI.RenderTexture.create({width: 512, height: 512}); - - - /*this.layers.push(new Layer( - this.app, base_render_texture, this.brushGenerator, "-1", - "0xFFFFFF"));*/ - for (var i = 0; i < player_data.length * 2; i++) { const render_texture = PIXI.RenderTexture.create({width: 512, height: 512}); @@ -205,5 +197,19 @@ export class DrawingApp { return this.app; } + addBackgroundForDownload() { + if(!this.hasBackgroundAlready) { + let rectangle = new PIXI.Graphics(); + rectangle.lineStyle(0.5, 0x999999); + rectangle.beginFill(0xFFFFFF); // draw each row of rectangles in different color :) + rectangle.drawRect(0, 0, 512, 512); + rectangle.endFill(); + + this.app.stage.addChildAt(rectangle, 0); + + this.hasBackgroundAlready = true; + } + } + lock_all_layers() {} } \ No newline at end of file diff --git a/client/src/index.html b/client/src/index.html index df7202d..6296381 100644 --- a/client/src/index.html +++ b/client/src/index.html @@ -25,6 +25,10 @@ color: #FFFFFF; } + .room_input { + text-transform: uppercase; + } + li { color: #FFFFFF; } @@ -40,6 +44,11 @@ justify-content: center; } + .table-center { + height: 100%; + justify-content: center; + } + #game_leader_input { display: none; } @@ -88,15 +97,59 @@ #pl_container { background-color: white; margin-bottom: 20px; + display: none; } label { color: white; } - #copy_image {} + #copy_image { + display: none; + } - #download_image {} + #download_image { + display: none; + } + + #player_list { + list-style-type: none; + } + + .player_ready_state { + width: 1.2vmax; + height: 1.2vmax; + } + + #ready_up_btn { + color: black; + } + + #ready_up_btn:disabled { + color: white; + } + + .additional_info { + font-size: 1.5vmax; + color: white; + margin-top: 50px; + } + + .additional_info>a:link { + color: red; + } + + .additional_info>a:hover { + color: green; + } + + .additional_info>a:active { + color: teal; + } + + .additional_info>a:visited { + color: yellow; + } @@ -111,7 +164,8 @@
    @@ -128,7 +182,7 @@
    - +
    @@ -137,18 +191,34 @@
    +
    - +
    + + + + + + + + + + + + + +
    VotesVote?NameTurnsHas Voted?
    @@ -161,10 +231,20 @@
    - +
    +
    + This website is a (shitty) implementation of the board game "A Fake Artist goes to New + York" by Oink Games Inc.
    + There is also an official digital version on Steam Let's Play! Oink Games +
    +
    + Sourcecode available on GitHub :) +
    \ No newline at end of file diff --git a/client/src/server/server.js b/client/src/server/server.js index 4a78e06..db8ca08 100644 --- a/client/src/server/server.js +++ b/client/src/server/server.js @@ -4,7 +4,7 @@ const http = require('http').Server(app); const io = require('socket.io')(http); const cors = require('cors'); -const room_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; +const room_chars = 'ABCDEFGHJKLMOPRSTUWXYZ'; Array.prototype.remove = function() { var what, a = arguments, L = a.length, ax; @@ -34,15 +34,23 @@ const available_colors = [ '#500027', '#006241', '#ffc4e4' ]; + +var active_games = {}; + + class Player { id = null; player_name = 'Player'; player_color = ''; + score = 0; + ready = false; constructor(id_, name_, player_color_) { this.id = id_; this.player_name = name_; this.player_color = player_color_; + this.score = 0; + this.ready = false; } } @@ -55,35 +63,398 @@ const GAME_STATE = { } class Game { + // Const room_id = ''; + + // Change on trigger topic = ''; hint = ''; + current_game_state = GAME_STATE.PRE_GAME; + ; + players = []; // Player Class - actual_players = []; - current_game_state = 0; - current_player = null; + disconnected_during_session = []; + leader = null; fake_artist = null; - current_round = 0; + // - Per turn + current_turn = 0; + current_player = null; + + // - Per Game player_turns = []; - disconnected_during_session = []; + possible_leaders = []; + actual_players = []; + spectators = []; + + turn_drawdata = []; // 2d array, turn -> then array of draw_data dicts + + can_vote = []; + player_votes = {}; constructor(room_id_) { this.room_id = room_id_ } - add_player(player) { + start_game() { + this.current_game_state = GAME_STATE.TOPIC_SELECTION; + + io.to(this.room_id).emit('game_started'); + + this.players.forEach((p) => { + p.ready = false; + }); + + this.select_leader(); + this.prepare_player_turns(); + this.select_fake_artist(); + this.notify_leader(); // to choose topic + + this.spectators = []; + } + + select_leader() { + const leader = + this.players[Math.floor(Math.random() * this.players.length)]; + + this.leader = leader; + + var remaining_players = this.players.filter(e => e !== leader); + this.actual_players = remaining_players.sort((a, b) => 0.5 - Math.random()); + } + + prepare_player_turns() { + this.player_turns = this.actual_players.concat(this.actual_players); + + this.turn_drawdata = + []; // new Array(this.player_turns.length).fill(new Array()); + for (var i = 0; i < this.player_turns.length; i++) { + this.turn_drawdata.push([]); + } + } + + select_fake_artist() { + const fake_artist = this.actual_players[Math.floor( + Math.random() * this.actual_players.length)]; + + this.fake_artist = fake_artist; + } + + notify_leader() { + this.topic = ''; + this.hint = ''; + + io.to(this.room_id).emit('leader_selected', this.leader); + } + + start_voting() { + this.current_game_state = GAME_STATE.VOTING; + this.player_votes = {}; + this.can_vote = + this.player_turns.slice(0, [Math.ceil(this.player_turns.length / 2)]); + + this.can_vote.forEach((p) => { + this.player_votes[p.id] = 0; + }); + + console.log('GAME FINISHED!'); + io.to(this.room_id).emit('game_finished', this.leader, this.can_vote); + } + + end_voting() {} + + update_player_list() { + io.to(this.room_id).emit('player_data', this.players); + } + + update_votes() { + var vote_data = {}; + + vote_data.players_voted = []; + + const already_voted_players = this.actual_players.filter( + a => !this.can_vote.map(b => b.id).includes(a.id)); + /*this.actual_players.filter( el => { + return this.can_vote.some( f => { + return f.id == el.id; + }); + });*/ + + // console.log(already_voted_players); + + already_voted_players.forEach((p) => { + vote_data.players_voted.push(p.id); + }); + + if (this.can_vote.length == 0) { + vote_data.done = true; + // everybody voted, send full data + vote_data.player_votes = this.player_votes; + vote_data.fake_artist = this.fake_artist; + this.current_game_state = GAME_STATE.RESULTS; + io.to(this.room_id).emit('show_vote_results', vote_data); + this.update_player_list(); + } + else { + io.to(this.room_id).emit('update_votes', vote_data); + } + } + + advance_turn() { + console.log('turn: ', this.current_turn); + + if (this.current_game_state == GAME_STATE.VOTING) { + return; + } + + this.current_player = this.player_turns[this.current_turn]; + + if (this.current_turn >= this.player_turns.length) { + this.start_voting(); + return; + } + + if (this.disconnected_during_session.includes(this.current_player.id)) { + this.current_turn++; + advance_turn(); + return; + } + + io.to(this.room_id) + .emit('advance_turn', this.current_turn, this.current_player); + + this.current_turn++; // because of this we have to -1 current_turn in some + // instances + } + + get_next_free_player_color() { + var player_color = available_colors[0]; + + for (var i = 0; i < available_colors.length; i++) { + var free = true; + for (var k = 0; k < this.players.length; k++) { + if (available_colors[i] == this.players[k].player_color) { + free = false; + break; + } + } + + if (free) { + player_color = available_colors[i]; + break; + } + } + + return player_color; + } + + all_players_ready() { + // Check if all players are ready + // and if we have at least 3 players... + if (this.players.length < 3) { + return false; + } + + const not_ready = this.players.filter((p) => p.ready == false); + if (not_ready.length > 0) { + return false; + } + + return true; + } + + is_spectator(player) { + return this.spectators.some((e) => { + return e.id == player.id; + }); + } + + add_player(socket_, name_) { + const player = + new Player(socket_.id, name_, this.get_next_free_player_color()); this.players.push(player); + + socket_.join(this.room_id); + + socket_.game = this; + + this.update_player_list(); + + if (this.current_game_state != GAME_STATE.PRE_GAME) { + console.log('adding spectator', player.player_name); + this.spectators.push(player); + } + + if (this.current_game_state == GAME_STATE.TOPIC_SELECTION) { + socket_.emit('game_started'); + socket_.emit('leader_selected', this.leader); + } + + // Send current game state data to socket + if (this.current_game_state == GAME_STATE.DRAWING || + this.current_game_state == GAME_STATE.RESULTS || + this.current_game_state == GAME_STATE.VOTING) { + socket_.emit('game_started'); + socket_.emit('actually_start_game', this.actual_players); + socket_.emit( + 'topic_selected', '? (You are a spectator. No cheating!)', this.hint); + for (var turn = 0; turn < this.current_turn; turn++) { + console.log('StateTurn: ', turn, this.turn_drawdata[turn].length); + socket_.emit('advance_turn', turn, this.player_turns[turn]); + for (var idx = 0; idx < this.turn_drawdata[turn].length; idx++) { + console.log('DrawData: ', turn, idx); + socket_.emit('draw_data', turn, this.turn_drawdata[turn][idx]); + } + } + // console.log(JSON.stringify(this.turn_drawdata)); + + if (this.current_game_state == GAME_STATE.RESULTS || + this.current_game_state == GAME_STATE.VOTING) { + socket_.emit( + 'game_finished', this.leader, + this.player_turns.slice( + 0, [Math.ceil(this.player_turns.length / 2)])); + } + } + + socket_.on('ready', () => { + console.log(socket_.id); + console.log(this.players); + const p = this.get_player(socket_.id); + if (p) { + p.ready = true; + this.update_player_list(); + + this.try_start_game(); + } + }); + + socket_.on('draw_data', (arr) => { + if (socket_.id != this.current_player.id) { + return; + } + + // console.log(this.current_turn - 1, arr); + this.turn_drawdata[this.current_turn - 1].push(arr); + + socket_.broadcast.to(this.room_id) + .emit('draw_data', this.current_turn - 1, arr); + }); + + socket_.on('topic_selected', (topic, hint) => { + if (this.current_game_state != GAME_STATE.TOPIC_SELECTION) { + return; + } + + console.log(topic, hint); + + if (socket_.id != this.leader.id) { + // Ignore other people trying to set a topic + return; + } + + if (topic.length < 2 || hint.length < 2) { + socket_.emit( + 'topic_error', + 'The provided topic/hint are too short (min. 2 characters)') + return; + } + + this.topic = topic; + this.hint = hint; + + console.log(this.spectators); + + this.players.forEach((player) => { + console.log(player.id); + if (this.is_spectator(player)) { + io.to(player.id).emit( + 'topic_selected', '? (You are a spectator. No cheating!)', hint); + return; + } + + if (player.id != this.fake_artist.id) { + console.log('OK'); + io.to(player.id).emit('topic_selected', topic, hint); + } else { + console.log('FAKE'); + io.to(player.id).emit( + 'topic_selected', '? (You are the Fake Artist!)', hint); + } + }); + + this.current_game_state = GAME_STATE.DRAWING; + io.to(this.room_id).emit('actually_start_game', this.actual_players); + this.current_turn = 0; + this.advance_turn(); + }); + + socket_.on('turn_finished', () => { + if (this.current_game_state != GAME_STATE.DRAWING) { + return; + } + + for (var turn = 0; turn < this.current_turn; turn++) { + console.log('DrawDataLength: ', turn, this.turn_drawdata[turn].length); + } + + this.advance_turn(); + }); + + socket_.on('vote_for_player', (player_id) => { + if (this.current_game_state != GAME_STATE.VOTING) { + return; + } + + if (this.can_vote.filter(e => e.id === socket_.id).length > 0) { + // do vote + this.can_vote = this.can_vote.filter(e => e.id !== socket_.id); + console.log(this.can_vote); + console.log('do vote'); + + this.player_votes[player_id]++; + + this.update_votes(); + } + }); + + socket_.on('disconnect', () => { + console.log('Player disconnected!'); + this.remove_player(socket_.id); + + // if this was the last player of our game + if (this.players.length == 0) { + delete active_games[this.room_id]; + return; + } + + this.try_start_game(); + }); + } + + try_start_game() { + if (this.all_players_ready()) { + this.start_game(); + } } remove_player(player_id) { this.players.splice( - this.players.findIndex(item => item.id === player_id), 1) + this.players.findIndex(item => item.id === player_id), 1); + this.update_player_list(); + } + + get_player(player_id) { + const idx = this.players.findIndex(item => item.id === player_id); + + if (idx != -1) { + return this.players[idx]; + } + + return null; } } -var active_games = {}; - /* GameLoop: - Lobby @@ -94,19 +465,11 @@ game) - They take turns, only getting to see the image once its their turn (may be changed, depending) on how boring it gets to wait (maybe only let game leade peek) - - Once everybody painted two strokes (two full rounds of the game) + - Once everybody painted two strokes (two full turns of the game) Reveal the image to everybody */ -initGame = - () => { - console.log('Initializing Game!'); - let count = 0; - // Game.map = {}; - // console.log("Map Created: ", Game.map); - } - - app.use(cors()); +app.use(cors()); app.use(express.static('static')); app.get('/', (req, res) => { @@ -116,19 +479,14 @@ app.get('/', (req, res) => { io.on('connection', (socket) => { console.log('User: ', socket.id, ' connected.'); - socket.on('draw_data', (arr) => { - if (socket.room) { - var room_id = socket.room; - var game = active_games[room_id]; - - socket.broadcast.to(game.room_id) - .emit('draw_data', game.current_round - 1, arr); - } - - console.log(arr[0], arr[1]); - }); - - // socket.on('') + // Create Room + // Join Room + // Ready Up (Pre-Game and for Replay) -> Start game + // Vote cast + // Topic selected (Topic + Hint) + // turn Finished (Finished Drawing, either by lifting the pen or using up all + // ink) Connect / Disconnect (disconnect is especially important to remove + // players from games/lobbies) socket.on('create_room', (username) => { if (username == '') { @@ -136,249 +494,62 @@ io.on('connection', (socket) => { return; } - console.log('New room'); var room_id = randomId(); - console.log(room_id); - socket.join(room_id); - socket.emit('room', room_id); - console.log(io.sockets.adapter.rooms); + while (room_id in active_games) { + var room_id = randomId(); + } var game = new Game(room_id); - var player_color = available_colors[0]; - - for (var i = 0; i < available_colors.length; i++) { - var free = true; - for (var k = 0; k < game.players.length; k++) { - if (available_colors[i] == game.players[k].player_color) { - free = false; - break; - } - } - - if (free) { - player_color = available_colors[i]; - break; - } - } - - var player = new Player(socket.id, username, player_color); - - game.add_player(player); - - socket.room = room_id; - active_games[room_id] = game; - io.to(room_id).emit('player_data', active_games[room_id].players); - console.log(JSON.stringify(active_games[room_id])); + + socket.emit( + 'join_room', + room_id); // maybe do this in the game instance instead? same as join + game.add_player(socket, username); + game.update_player_list(); }); - socket.on('start_game', () => { - if (socket.room) { - var room_id = socket.room; - var game = active_games[room_id]; - - if(game.game_state == GAME_STATE.TOPIC_SELECTION) { - // TODO: Check what happens after replay - return; - } - - game.game_state = GAME_STATE.TOPIC_SELECTION; - - io.to(room_id).emit('game_started'); - - var leader = - game.players[Math.floor(Math.random() * game.players.length)]; - game.leader = leader; - - var remaining_players = game.players.filter(e => e !== leader); - game.actual_players = remaining_players.sort((a, b) => 0.5 - Math.random()); - - - - game.player_turns = game.actual_players.concat(game.actual_players); - - var fake_artist = remaining_players[Math.floor( - Math.random() * remaining_players.length)]; - - game.fake_artist = fake_artist; - - io.to(room_id).emit('leader_selected', leader); - } - }); - - socket.on('topic_selected', (topic, hint) => { - console.log(topic, hint); - - if (socket.room) { - var room_id = socket.room; - var game = active_games[room_id]; - - if(game.game_state != GAME_STATE.TOPIC_SELECTION) { - return; - } - - if(socket.id != game.leader.id) { - // Ignore other people trying to set a topic - return; - } - - if(topic.length < 2 || hint.length < 2) { - return; - } - - game.topic = topic; - game.hint = hint; - - game.players.forEach((player) => { - console.log(player.id); - if (player.id != game.fake_artist.id) { - console.log('OK'); - io.to(player.id).emit('topic_selected', topic, hint); - } else { - console.log('FAKE'); - io.to(player.id).emit('topic_selected', '', hint); - } - }); - - game.game_state = GAME_STATE.DRAWING; - io.to(socket.room).emit('actually_start_game', game.actual_players); - - - // FIXME: Super slow for some reason - // Maybe just do the following: - // Have a manual advance on the clients (button, ink empty, on mouse up) - // If someone disconnects, just manually advance, no timeout needed - game.current_round = 0; - - // game.current_player = game.player_turns[game.current_round]; - // socket.to(socket.room).emit('advance_round', game.current_round, - // game.current_player); - - play_next_turn(game); - } - }); - - function play_next_turn(game) { - console.log('Round: ', game.current_round); - - if (game.game_state == GAME_STATE.VOTING) { - return; - } - - if (game.current_round >= game.player_turns.length) { - game.game_state = GAME_STATE.VOTING; - console.log('GAME FINISHED!'); - io.to(game.room_id).emit('game_finished', game.leader, game.player_turns.slice(0, [Math.ceil(game.player_turns.length / 2)])); - game.game_state = GAME_STATE.VOTING; - // TODO: Pass fake artist after voting... implement voting in the first - // place - return; - } - - //while(game.current_player) - - game.current_player = game.player_turns[game.current_round]; - io.to(game.room_id) - .emit('advance_round', game.current_round, game.current_player); - - game.current_round++; - } - - socket.on('round_finished', () => { - if (socket.room) { - var room_id = socket.room; - var game = active_games[room_id]; - - if (game.game_state != GAME_STATE.DRAWING) { - return; - } - - console.log('round finished!'); - play_next_turn(game); - } - }); - - // socket. - socket.on('join_room', (room_id, username) => { + var upper_room_id = room_id.toUpperCase(); + if (username == '') { socket.emit('room_error', 'Invalid username!'); return; } - if (!active_games[room_id]) { + if (!active_games[upper_room_id]) { socket.emit('room_error', 'This room does not exist!'); return; } - var user_count = active_games[room_id].players.length; + var user_count = active_games[upper_room_id].players.length; - if (user_count > 10) { + if (user_count >= 10) { socket.emit('room_error', 'This room is full!'); return; } - var game_state = active_games[room_id].current_game_state; - - if (game_state != GAME_STATE.PRE_GAME) { - socket.emit('room_error', 'This game is already in progress!'); - return; - } - - - var game = active_games[room_id]; - - socket.join(room_id); - - var player_color = available_colors[0]; - - for (var i = 0; i < available_colors.length; i++) { - var free = true; - for (var k = 0; k < game.players.length; k++) { - if (available_colors[i] == game.players[k].player_color) { - free = false; - break; - } - } - - if (free) { - player_color = available_colors[i]; - break; - } - } - - var player = new Player(socket.id, username, player_color); - active_games[room_id].add_player(player); - - socket.room = room_id; - - socket.emit('room', room_id); - io.to(room_id).emit('player_data', active_games[room_id].players); - console.log(JSON.stringify(active_games[room_id])); + var game = active_games[upper_room_id]; + game.add_player(socket, username); + socket.emit( + 'join_room', + upper_room_id); // maybe do this in the game instance instead? + game.update_player_list(); // this could be automated then... }); + // TODO: Refactor/Check what's happening socket.on('disconnect', () => { console.log('User: ', socket.id, ' disconnected.'); - if (socket.room) { - var room_id = socket.room; - var game = active_games[room_id]; - game.remove_player(room_id); - io.to(room_id).emit('player_data', game.players); - - console.log(socket.id, game.current_player); - - if (game.game_state == GAME_STATE.DRAWING) { - game.disconnected_during_session.push(game.players[socket.id]); - - /*console.log(game.player_turns); - game.player_turns = game.player_turns.filter(e => e.id != socket.id); - console.log(game.player_turns);*/ - if (socket.id == game.current_player.id) { - play_next_turn(game); - } + if (socket.game) { + var room_id = socket.game.room_id; + const clients = io.sockets.adapter.rooms.get(room_id); + if (clients) { + for (const clientId of clients) { + console.log(clientId); } + } } }); }); @@ -386,6 +557,4 @@ io.on('connection', (socket) => { http.listen(7777, '0.0.0.0', () => { console.log('Listening on 7777'); -}) - -initGame(); +}) \ No newline at end of file